1

I have a basic class like this:

public class task
    {
        public string tName;
        // Construct
        public job(string t)
        {
            tName = t;
        }
    }

and currently I can do this:

public class TASKCLASS
    {

        private task myTaskName1 = new task("name of task 1");

        public Dictionary<string, task> tasks = new Dictionary<string, task>();

        // Construct
        public TASKCLASS()
        {
            tasks.Add(myTaskName1.tName, myTaskName1);
        }
    }

Great so that works! But I have a lot of tasks to add to the dictionary - now I know I can't add my tasks directly in the .Add section like so:

public class TASKCLASS
    {

        public Dictionary<string, task> tasks = new Dictionary<string, task>();

        // Construct
        public TASKCLASS()
        {
            tasks.Add("name of task 1", myTaskName1 = new task("name of task 1"));
        }
    }

Because the object myTaskName1 doesn't exist even though I'm trying to create it in the .Add line

But I'm wondering if there is any way I can structure my task class so that I can both create a new one with the tName and have it added to my dictionary in the same line to prevent DRY errors somewhere down the line?

IDEA: Can I create a new method for my jobs dictionary so that the .Add function will simply take the tName and the name of the task object?

EDIT: I think I might just use task as a base class and derive a bunch of classes called the handy names I want. I can probably cycle through a list of objects and look at their properties fairly easily while also being able to call each instance by their name at any time.

15
  • 1
    Create a task factory that returns an array of tasks. Then use tasks.AddRange(taskArray); Commented Jul 9, 2018 at 14:33
  • 1
    well why can't you say tasks.Add("name of task 1", new task("name of task 1")); Commented Jul 9, 2018 at 14:35
  • 1
    You create it during the Add method by newing it up. Otherwise, it's not very clear what you're asking, what you're trying to accomplish and why. Commented Jul 9, 2018 at 14:42
  • 1
    Side note: your task class exposes its tName as a publicly mutable field. That means that whatever value it had when it was added to the dictionary isn't necessarily the name it currently has. Commented Jul 9, 2018 at 14:44
  • 1
    You could use a keyedcollection. Or write a class that inherits from Dictionary. Commented Jul 9, 2018 at 14:50

3 Answers 3

1

You can just create a constructor that accepts a task name to the task class. In addition you can add a method that adds a new task to the dictionary in your class that is tracking the tasks. You can create a second method that adds to your dictionary from a list of tasks.

Added based off comment: You can add constants that if there are tasks that are special cases, and you are worried about mistyping their names.

You could also inherit from Task and create derived tasks that have reserved names. But this would mean that the derived class would be limited to a single instance in your dictionary. I'm not sure if that's what you want though. And inheriting from a base class to maintain a name seems like overkill to me.

public class Task
{
    //protected task name that can only be set in base or derived classes
    protected string name;

    //Property to return task name
    public string Name { get { return name; } }

    public Task() {

    }

    //Task contructor that requires a name value to be instantiated
    public Task(string name)
    {
        this.name = name;
    }
}

public class DerivedTask1 : Task
{
    public static const string TASK_NAME = "Derived Task Name 1";

    public DerivedTask1() 
    {
        this.name = TASK_NAME;
    }
}

public class TaskTracker
{ 
    //Private dictionary to hold tasks
    private Dictionary<string, Task> tasks;

    public TaskTracker()
    {
        this.tasks = new Dictionary<string, Task>();
    }

    public void AddTask(Task task) {
        //Adds a task to the dictionary, note that there is no error checking and will fail when task with duplicate name is added
        this.tasks.Add(task.Name, task);
    }

    public void AddTasks(List<Task> newTasks)
    {
        //Adds all tasks in a list to the backing task dictionary
        foreach(Task task in newTasks)
        {
            AddTask(task);
        }
    }

    public Task GetTask(string taskName)
    {
        if (this.tasks.ContainsKey(taskName))
        {
            return this.tasks[taskName];
        }
        else
        {
            return null;
        }
    }
}

Code example adding tasks to your collection:

const string RESERVED_TASK_NAME_1 = "Reserved Task Name 1";
const string RESERVED_TASK_NAME_2 = "Reserved Task Name 2";

var tracker = new TaskTracker();
tracker.AddTask(new Task("Task 1"));
tracker.AddTask(new Task("Task 2"));

var taskList = new List<Task>();
taskList.Add(new Task(RESERVED_TASK_NAME_1));
taskList.Add(new Task(RESERVED_TASK_NAME_2));

tracker.AddTasks(taskList);

var taskFromLookup = tracker.GetTask(RESERVED_TASK_NAME_1);

tracker.AddTask(new DerivedTask1());
var otherTaskFromLookup = tracker.GetTask(DerivedTask1.TASK_NAME);
Sign up to request clarification or add additional context in comments.

6 Comments

Thanks for the reply, I think I will have to take this approach - I really wanted to have my task1,2,3 instances have unique codenames identifiable by intellisense so I can't mistype their "string name" later on in code, but I feel like what I'm asking is just not a common thing. I'm sure it's possible, I did something similar with string codenames using reflection a while ago so will revisit that. Thanks again anyway, upvoted!
Is the only aim for avoiding mistypes and catching missing names at compile time? If so you can just add constant variables that are the reserved task names and only use those. An example has been added. However I still think I am missing something from your objective.
@jamheadart I think you're seeking a short-cut where one doesn't really exist (nor is needed). I guess you could approximate this with a list, or a dictionary keyed by integer or a custom enum, e.g., MyTasks[1] instead of myTaskName1. You can create the dictionary as a custom class if you need additional control over it, etc. And while you can do some dynamic stuff with reflection, I'm not sure that would help w/intellisense at design time, it might even make it worse (abstract/factory classes, etc.)
See the myTask1 nomenclature is just for example, the tasks will be called something more "meaningful" and without a numerical scale - perhaps it doesn't exist... it feels like it should. I want 20-30 instances of my various "task" objects with various properties, all with a meaningful codename so I can refer to them many times in my code and pull their respective properties. I just also wanted them added to a list-type for easy cycling through to check properties and retrieve the name of the object. Guess it just doesn't work that way.
Also I just realised the Const idea for object names - might be very useful
|
1

IDEA: Can I create a new method for my jobs dictionary so that the .Add function will simply take the tName and the name of the task object?

There's a few problems with this.

The assignment statement myTaskName1 = new task("name of task 1") returns a void, so even if it were possible to create variable instances in an enclosing scope from within the enclosed scope, your dictionary would ultimately not have any Values. This might not bother you, but obviously you cannot reference a variable before it exists, which is what you're trying to do, with the added flare of trying to also create that variable in an outside scope!

If you're merely trying to get some assistance from intellisense to manage dozens or hundreds of fields/properties/variables representing instances of a particular class, I think your problem is the application design. You're trying to avoid writing spaghetti code, which is admirable, but you're still thinking in spaghetti :D

In a nutshell, though, if you've got dozens or hundreds of "task" variables, intellisense isn't going to really help you any more than using an indexed list or a keyed dictionary to manage those. While you can do some funny stuff with Reflection and abstract classes, I'm not sure that's going to get you anywhere close to what you want, and may actually make things worse!

Just to present another option, you could approximate this with an enum, like:

MyTasks.cs:

    public enum MyTasks
    {
        taskName1,
        taskName2,
        taskName3,
        taskName4,
        taskName5,
        taskName6 // Etc...
    }

TaskDict.cs

public class TaskDict: Dictionary<MyTasks, Task>

If you have certain default tasks that you always need, then just add a constructor method to TaskDict:

    public TaskDict()
    {
        Add(MyTasks.taskName1, new Task("name of task 1"));
        Add(MyTasks.taskName2, new Task("name of task 2"));
        Add(MyTasks.taskName3, new Task("name of task 3"));
        // etc.
    }

As you can see, TaskDict is a dictionary keyed to the MyTasks enum, with Task instances in the values.

Task.cs

public class Task
{
    string tName;
    public Task(string taskName)
    {
        tName = taskName;
    }
    public void PrintName()
    {
        Console.WriteLine(tName);
    }

}

Now in your tracker class, just new a TaskDict as a property/field:

TaskTracker.cs

public class TaskTracker
{
    public TaskDict Tasks = new TaskDict();

    public TaskTracker()
    {
        // You could add tasks here if needed
        Tasks.Add(MyTasks.taskName5, new Task("the fifth task"));
        // Or remove them:
        Tasks.Remove(MyTasks.taskName1);

        _run();
    }
    private void _run()
    {
        foreach(MyTasks t in Tasks.Keys)
        {
            Tasks[t].PrintName();
        }
    }
}

2 Comments

Thanks again for the comprehensive reply. I'll dedicate some proper time tomorrow to go through this and understand it properly.
But yeah I definitely realise I can't use my object or one of its properties before I've even created it... I just see so much clever one-liner stuff done with C# I thought there may be some clever syntax to do what I was doing in a loop instead of repeating lists.
0

I've re-thunk this and the way to achieve what I really want is actually to create an abstract class with all those functions I need, and then create derived, empty classes with the name I want to persist and have intellisense access to.

Because they're just derived, I can have additional functions if necessary particular to the job which is where my code was going anyway... added bonus!

I guess I'm just not used to breaking every single thing down in to classes yet - not sure if this is tending away or to spaghetti but let's see how it pans out...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.