dreza
dreza

Reputation: 3645

Add to list when list type is not known until runtime

I have method which accepts an object. This object I know is a List<T> however T may vary between children of a base class at any one time when being passed into the method.

So if my base class is MonthType, and I have children called BlockMonthType and AreaMonthType the object passed in could be anyone of List<BlockMonthType> or List<AreaMonthType>.

I want to be able to add items to this object however when I cast it it seems to make a copy and the original object is not updated.

I'm doing this to cast:

var objectList = ((IEnumerable<MonthType>)graphObject.Source.Object).ToList();

Now I want to create a new item and add it to the list

// where ObjectType is a Type variable containing BlockMonthType
var newObject = (BlockMonthType)Activator.CreateInstance(graphObject.Source.ObjectType);

objectList.Add(newObject);

// and carry on the world is good

This works in so far as objectList has a newObject added. However the original variable isn't updated so when I leave the method it's back to it's original state. I know the object is a List<> when passed in as I can see it in the debugger as such.

Is there anyway I can accomplish this?

Here is a cut down version of the method I'm using it in.

public TraverseGraphResult Write(ObjectGraph graphObject)
{
    var objectList = ((IEnumerable<MonthType>)graphObject.Source.Object).ToList();

    var newObject = (MonthType)Activator.CreateInstance(rule.ObjectType);
    newObject.Month = rule.Month;

    objectList.Add(newObject);

    // Other stuff as well is done but that's the crux of it
}

Hopefully this gives it more context. The method is being used to try and navigate a large object tree with many class types. I'm trying to add a new class type handler which will deal with adding and removing items from a list.

// This is being used in a recursive method to loop down a object's property tree

// .. more code here

// where properties is a List<PropertyInfo>
foreach (var pInfo in properties)
{
    if (IsList(pInfo.PropertyType))
    {
        var enumerable = (IEnumerable)pInfo.GetValue(currentObjectGraph.Source.Object, null);

        var sourceEnumerator = enumerable.GetEnumerator();          
        var graph = new ObjectGraph(enumerable, pInfo.Name);

        // this part is made up but essentially the code looks up a list of objects that can deal with this 
        // particular one and returns it.  We then call the write method on that object
        var something = GetInterfaceHandlerForObject(enumerable);
        something.Write(graph);
    }
}

Upvotes: 1

Views: 137

Answers (2)

dreza
dreza

Reputation: 3645

I ended up resolving this by storing the underlying List T type in the ObjectGraph object and casting to that when required.

var objectList = ((IEnumerable)graphObject.Source.Object).Cast(monthAllocationRule.ListType);

Without the correct cast objectList was either null or a copy of the list. Now I can add to objectList and know it's added to the source object.

Probably not idea as Ian mentioned above but did the trick.

Upvotes: 0

Ian Mercer
Ian Mercer

Reputation: 39277

You should make your method generic:

public void MyMethod<T>(List<T> objectList) where T:class, new()
{
    objectList.Add(new T());
    ...
}

Casting is rarely ever necessary when you use generics. Also, your ToList() is causing a new copy of the list to be created.

One drawback to this approach is that T needs to have an empty constructor. If you need to construct an object with parameters you could instead pass in a Func<T>. You can then call it passing in a lambda expression like: (x) => new BlockMonthType(someParameter, orAnother).

Upvotes: 4

Related Questions