I have dictionary that gives me back a method according to the value passed in. Defined as so:
Dictionary<Type, IXmlWriterConverter>
I have now added a new function that which has the Key/type set to IEnumerable, so far so good.
But when I execute my unit test with a List containing two DataTables but the dictionary can not find the key e.g. my type conversion differs.
Why is that so? And what would be the right attempt to solve my problem?
Edit: Sorry here is the requested code ;-)
Function that generates the testvalues:
public IEnumerable<DataTable> CreateTestDataTableList()
{
var resultDataTable = new List<DataTable>();
resultDataTable.Add(CreateTestTable("testTable1", 2));
resultDataTable.Add(CreateTestTable("testTable2", 3));
return resultDataTable;
}
Function called by the unit test:
public void Write(XmlWriter xmlWriter, object value)
{
...
converter = FindConverter(value.GetType());
}
Function checking the dictionary:
public IXmlWriterConverter FindConverter(Type type)
{
if(Converters.ContainsKey(type))
{
return Converters[type];
}
return null;
}
2.Edit:
Code that adds the values to the Dictionary:
public void Add(IXmlWriterConverter xmlWriterConverter)
{
if(Converters.ContainsKey(xmlWriterConverter.InputType))
{
Remove(xmlWriterConverter);
}
Converters.Add(xmlWriterConverter.InputType, xmlWriterConverter);
}
The InputType is a readonly (get) property of the converter. I checked the type added to the dictionary and that was registered as IEnumerable, however when I checked on the typeof when passing in my list the type was List and not IEnumerable. I was told that this happens because I pass in the values as object.
This is a really code-stinky solution to me, and it cuts down on the efficiency, but you can also iterate through the GetInterfaces() method on Type, like this:
List<DataTable> l = new List<DataTable>();
var t = l.GetType();
var ints = t.GetInterfaces();
Then you could do a lookup on the type, and, if that doesn't work do a lookup on it's interfaces.
However, this feels like a terrible hack, which usually indicates that some more design work needs to be done. Is it not possible to put the List type in the dictionary? Is there no better way of doing this lookup?
Also, a note on doing dictionary lookups: It's more efficient to use the TryGetValue method, like this:
public IXmlWriterConverter FindConverter(Type type)
{
IXmlWriterConverter converter;
if( Converters.TryGetValue(type, out converter) )
{
return converter;
}
return null;
}
When you do it this way, it only does one lookup on the dictionary, whereas if you use ContainsKey it has to do two lookups.
Its a bit of hack but the only thing that gets into my mind when looking at your code, is to add an other generic write method:
public void Write<TValue>(XmlWriter writer, TValue value) {
// ...
}
This allows to identify the right type for IEnumerable and leaves the other Write method to not break any existing code.
Probably you are trying to retrieve List type and not IEnumerable (inheritance is not going to work in this context)
Please paste the code that does the lookup if you want more details and more certain answer :)
You should also post the code of where you are retrieving the test values. But based on what you've given, are you possibly using the non-generic IEnumerable to retrieve the values instead of the generic version that you're using to generate them?
The InputType is a readonly (get)
property of the converter. I checked
the type added to the dictionary and
that was registered as IEnumerable,
however when I checked on the typeof
when passing in my list the type was
List and not IEnumerable. I was told
that this happens because I pass in
the values as object.
Like they say on MythBusters, well, there's your problem! Even though List is-a IEnumerable the Type objects that represent each are definitely not the same. In programmer jargon, typeof(List) != typeof(IEnumerable). That is why the lookup is not working.
Related
When using IEnumerable I'm trying to avoid multiple enumerations. I know I can just use LINQ's .ToList() and be done with it, but that can be a lot of unnecessary list creation. I'd like to:
check and see if the underlying type is a List, and if so return that instance, otherwise
.ToList() it and return the new List
My thought was to use something akin to:
public void Fee()
{
var list = new List<string>(); // I want to retrieve this instance in Foo
Foo(list);
}
public void Foo(IEnumerable<T> enumerable)
{
var list = enumerable as List<T> ?? enumerable.ToList();
// do stuff with original list
}
... but it appears from the documentation that the as operator just performs a cast, which would create a new List rather than returning the underlying one, would it not?
If so, how can I retrieve the underlying list instead of creating a new one?
The as operator does not create a new list. It only checks type and perform cast if type is compatible.
The code in the post is logically correct and matches how many LINQ methods are implemented (for example see source of Enumerable.Count which casts to ICollection to see if it can skip enumeration of items).
Note that it is important to cast to correct generic version of list or maybe one of its interfaces - IList would work if you must use non-generic version. Beware of the fact that List<T> is not co/contra-variant and type must match exactly unlike in case of covariant IEnumerable<out T> where you can cast parameter to IEnumerable<TBase> if IEnumerable<TDerived> passed.
Maybe you wanted to do this:
public void Fee()
{
var list = new List<string>(); // I want to retrieve this instance in Foo
Foo(list);
}
public void Foo<T>(IEnumerable<T> enumerable)
{
List<T> list = enumerable as List<T> ?? enumerable.ToList();
// do stuff with original list
}
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);
}
}
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).
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.
I have a string representation of a type, and I need to get a list of that specific type. I am trying this:
var string_rep = "Double";
var list = _context.Entity.ToList<Type.GetType(string_rep)>();
I am getting "Operator < cannot be applied to operands of type method group and System.Type". What's the right way of doing it? Appreciate it.
Since there's no way of typing the list variable to the actual generic type of the list if that type isn't known until runtime, there's no real benefit to having the list strongly typed to begin with. You may as well just use:
var list = _context.Entity.ToList<object>();
There is no way for you to get any more compile-time support than from using that method.
While you can use reflection to create the list, for example by using:
public static IList ToList(this IEnumerable source, string typeName)
{
return ToList(source, Type.GetType(typeName));
}
public static IList ToList(this IEnumerable source, Type type)
{
var list = (IList)Activator.CreateInstance(
typeof(List<>).MakeGenericType(type));
foreach(object item in source) list.Add(item);
return list;
}
The only advantage that you would have using one of these two over the first example is that adding an item to the list that is not of the type specified would throw an exception, as opposed to allowing the list to contain any type of object. Usually this isn't important, but it may be better to have the exception right in your face over allowing items that shouldn't exist into the list.
You can use Reflection to do this at runtime. See the following post:
C# generic list <T> how to get the type of T?
As the easiest way to convert the IList<T1> to IList<BaseT1>?
IList<T1>.Count() is very large number!!!
class BaseT1 { };
class T1 : BaseT1
{
static public IList<BaseT1> convert(IList<T1> p)
{
IList<BaseT1> result = new List<BaseT1>();
foreach (BaseT1 baseT1 in p)
result.Add(baseT1);
return result;
}
}
You'll get much better performance in your implementation if you specify the size of the result list when it is initalized, and call the Add method on List<T> directly:
List<BaseT1> result = new List<BaseT1>(p.Count);
that way, it isn't resizing lots of arrays when new items get added. That should yield an order-of-magnitude speedup.
Alternatively, you could code a wrapper class that implements IList<BaseT1> and takes an IList<T1> in the constructor.
linq?
var baseList = derivedList.Cast<TBase>();
Edit:
Cast returns an IEnumerable, do you need it in a List? List can be an expensive class to deal with
IList<T1>.Count() is very large number!!!
Yes, which means that no matter what syntax sugar you use, the conversion is going to require O(n) time and O(n) storage. You cannot cast the list to avoid re-creating it. If that was possible, client code could add an element of BaseT1 to the list, violating the promise that list only contains objects that are compatible with T1.
The only way to get ahead is to return an interface type that cannot change the list. Which would be IEnumerable<BaseT1> in this case. Allowing you to iterate the list, nothing else. That conversion is automatic in .NET 4.0 thanks to its support for covariance. You'll have to write a little glue code in earlier versions:
public static IEnumerable<BaseT1> enumerate(IList<T1> p) {
foreach (BaseT1 item in p) yield return item;
}
I have a custom list which inherits from Generic.List<T> like this:
public class TransferFileList<T> : List<TransferFile> { .. }
When I set (where 'Files' is a TransferFileList<T>):
var files = uploadResponse.Files.Where(x => !x.Success).ToList()
the 'files' object resolves as System.Collections.Generic.List<TransferFile>, not TransferFileList<T>, which is what I would expect as it was what was being filtered through the Where, so how could I successfully return a list of TransferFileList<T> into 'files'?
I did try:
var files = uploadResponse.Files.Where(x => !x.Success).ToList()
as TransferFileList<TransferFile>;
but using that safe cast, it just resolves as null.
Thanks guys and gals.
First, I have to ask why you are inheriting from List<T>? 99% of the time that's a bad idea.
If you want to extend the functionality of a list, use extension methods:
public static something PrintErrors(this List<TransferFile> list)
{
//do your printing logic
}
On to the answer: ToList() operates on an IEnumerable<T> and converts the members of the sequence to a List of the same type. Since you inherit from List<T> which implements IEnumerable<T>, that's what happens there.
Where() works the same way - operates on an IEnumerable<T> and returns an IEnumerable<T>.
To get some arbitrary list-like object back, like you have, you need to add the items in a sequence to your custom list, like so:
var myFiles = new TransferFileList<TransferFile>();
myFiles.AddRange(originalFileList.Where(condition));
You can add an extension method for IEnumerable<TransferFile> to handle that scenario:
public static TransferFileList ToTransferFileList(
this IEnumerable<TransferFile> files)
{
return new TransferFileList(files);
}
// ...
var files = uploadResponse.Files.Where(x => !x.Success).ToTransferFileList();
This provides you with the TransferFileList instead of just a List<TransferFile>. Note the reason your as returns null is because while TransferFileList is a List<TransferFile>, the same does not hold in the other direction. That is, your List<TransferFile> is NOT a TransferFileList object.
I agree with #RexM that any attempt at subclassing List<T> be avoided due to the multitude of pitfalls associated. I suggest Composition (Has-A rather than Is-A) or sticking with the base class library collections instead.
Thanks guys.
I like SLV's extension approach, but is there any other straight casting approach?
If not I might just go with the reverted in-line approach I was hoping to avoid:
var transferFiles = new TransferFileList<TransferFile>();
if (files != null)
transferFiles.AddRange(files);