Generic function throws cast exception [duplicate] - c#

I've created two classes, with one of them having an implicit cast between them:
public class Class1
{
public int Test1;
}
public class Class2
{
public int Test2;
public static implicit operator Class1(Class2 item)
{
return new Class1{Test1 = item.Test2};
}
}
When I create a new list of one type and try to Cast<T> to the other, it fails with an InvalidCastException:
List<Class2> items = new List<Class2>{new Class2{Test2 = 9}};
foreach (Class1 item in items.Cast<Class1>())
{
Console.WriteLine(item.Test1);
}
This, however, works fine:
foreach (Class1 item in items)
{
Console.WriteLine(item.Test1);
}
Why is the implicit cast not called when using Cast<T>?

Because, looking at the code via Reflector, Cast doesnt attempt to take any implicit cast operators (the LINQ Cast code is heavily optimised for special cases of all kinds, but nothing in that direction) into account (as many .NET languages won't).
Without getting into reflection and other things, generics doesnt offer any out of the box way to take such extra stuff into account in any case.
EDIT: In general, more complex facilities like implicit/explict, equality operators etc. are not generally handled by generic facilities like LINQ.

You can also use this to do casting with conversions if needed:
public static IEnumerable<TDest> CastAll<TItem, TDest>(this IEnumerable<TItem> items)
{
var p = Expression.Parameter(typeof(TItem), "i");
var c = Expression.Convert(p, typeof(TDest));
var ex = Expression.Lambda<Func<TItem, TDest>>(c, p).Compile();
foreach (var item in items)
{
yield return ex(item);
}
}
From http://adventuresdotnet.blogspot.com/2010/06/better-more-type-safe-alternative-to.html

Thanks for that I was about to use that exact case somewhere. You have saved me a pile of time. As a possible solution to your problem you could use ConvertAll<> instead, like so:
foreach (Class1 item in items.ConvertAll<Class1>((i) => (Class1)i))
{
Console.WriteLine(item.Test1);
}
EDIT: or if you want to be more explicit that the cast is implicit then this works too:
foreach (Class1 item in items.ConvertAll<Class1>(i => i))
{
Console.WriteLine(item.Test1);
}

A solution could be to use a bit of linq'ing here if you really need this kind of conversion:
List items = new List{new Class2{Test2 = 9}};
foreach (Class1 item in (from x in items select (Class1)x))
{
Console.WriteLine(item.Test1);
}

Related

Concatenating Lists at runtime

I am handling classes that wrap collections. For example:
public class CollA
{
public List<SomeType> Items {get;set;}
// other properties I'm not interested in
}
I am guaranteed that the collection classes will have exactly ONE property that is of List<T>
Now, I find myself with a requirement such that I may have many instances of CollA and I am asked to return a new instance of CollA where the property Items contains a union of the Items properties of the individual CollA instances. So, for example:
var A = new CollA(Items = new List<SomeType>
{
new SomeType("A"), new SomeType("B")
};
var B = new CollA(Items = new List<SomeType>
{
new SomeType("C"), new SomeType("D")
};
var result = SomeMythicalCombine(A, B);
// result.Items == { new SomeType("A"), new SomeType("B"), new SomeType("C"), new SomeType("D") }
This, if the types are all known at compile time is easy, but I need to do it with the types not being known until runtime.
I've got part of the way, I think, using reflection....
public T SomeMythicalCombine (params object[] collections)
{
var collectionType = typeof(T);
var listProperty = collectionType.GetProperties()
.Single(p=> typeof(IList).IsAssignableFrom(p.PropertyType));
var listPropertyName = listProperty.Name;
var result = Activator.CreateInstance(collectionType);
var innerType = listProperty.PropertyType.GenericTypeArguments[0];
var listType = typeof(List<>).MakeGenericType(innerType);
var list = Activator.CreateInstance(listType);
foreach(var collection in collections)
{
var listValues = collection.GetType().GetProperty(listPropertyName).GetValue(collection);
// listItems is an object here and I need to find a way of casting it
// to something I can iterate over so I can call (list as IList).Add(something)
}
// Then, I think, all I need to do is set the appropriate property on the
// the result item
result.GetType().GetProperty(listPropertyName).SetValue(result, list);
return result as T;
}
Can anyone fill in the gap in my thinking, please?
So basically if you know the type at compile time, you can do this:
var result = new CollA { Items = new[] { A, B }.SelectMany(c => c.Items).ToList() };
If you can require all your collection wrappers to implement an interface, it should be pretty simple to extract this into a generic method.
public interface ICollectionWrapper<T> { List<T> Items { get; set; } }
T SomeMythicalCombine<T, T2>(params T[] wrappers) where T : ICollectionWrapper<T2>, new()
{
return new T() { Items = wrappers.SelectMany(w => w.Items).ToList() };
}
That presupposes you can call the method with the right generic parameter. If your calling code knows the types of the collections you're dealing with, you can do this:
var result = SomeMythicalCombine(A, B);
But honestly if your calling code knows that, you might be better off using the first code snippet: it's concise and clear enough. Assuming you literally have a collection of objects that you just happen to know will all have the same run-time type, you should be able to use a little reflection to get that type and invoke the helper method with the right generic parameters. It's not ideal, but it might be faster/simpler than writing the entire method to work using reflection.
you can do this : var combined = A.Items.Concat(B.Items).
However, if the property is a part of interface or base class implementation, then you can target the implementation instead something like this :
public IList<TResult> SomeMythicalCombine<TResult>(params IInterface[] collection) // use interface or base class
{
// assuming that all collection would have the same element type.
}
if it is not a part of other implementations, then you can implement an interface and apply it to all classes, this would be an insurance that this collection will always be there as long as the class implements the interface.
if it's hard to achieve that, then you can and you see that reflection is your best option, you can use something like this :
// assuming all collections have the same property of type List<TResult> type.
// if they're different, then return an object instead. and change List<TResult> to IList
public IEnumerable<TResult> CombineLists<T, TResult>(params T[] instances)
where T : class
{
if (instances?.Any() == false) yield break;
foreach(var obj in instances)
{
if (obj == null) continue;
var list = obj.GetType()
.GetProperties()
.FirstOrDefault(p => typeof(List<TResult>).IsAssignableFrom(p.PropertyType))
?.GetValue(obj) as List<TResult>;
if (list?.Count == 0) continue;
foreach (var item in list)
yield return item;
}
}
usage :
var combined = CombineLists<CollA, string>(A, B);

In C#, how can I convert my array from IEnumerable<IMyInterface> to IEnumerable<T>?

In C#, I want to take an array of Type "T" where I know "T" supports the interface "IMyInterface" and:
Cast it as array of "IMyinterface"
Call a method on that array that will filter the list
Cast it back to the original type T list.
1 and 2 above work fine, but I am running into issues on step #3.
Here is my code:
IEnumerable<IMyInterface> castedArray = originalTypedArray as IEnumerable<IMyInterface>;
if (castedArray != null)
{
var filteredArray = castedArray.Where(r => r.Ids.Contains(MyId)).ToList();
IEnumerable<T> castedBackToOriginalTypeArray = filteredArray as IEnumerable<T>;
if (castedBackToOriginalTypeArray == null)
{
current = new List<T>();
}
else
{
current = castedBackArray;
}
// I need to cast back, because only my Type T has the .Id property
List<int> ids = current.Select(r => r.Id).ToList();
}
The issue is on this line:
IEnumerable<T> castedBackToOriginalTypeArray = filteredArray as IEnumerable<T>;
That always seem to return null (instead of the filtered array cast back to IEnumerable<T>.
Any suggestions here for what I might be doing wrong and how to correct cast an array of an interface back into an array of type T?
This works for me:
public class A : IA {
}
public interface IA {
}
List<A> l = new List<A> { new A(), new A(), new A() };
IEnumerable<IA> ias = l.Cast<IA>();
IEnumerable<A> aTypes = ias.Cast<A>();
Either you don't need to cast it to IEnumerable<IMyInterface>, or the runtime has correctly prevented you from writing buggy code.
Let's take a smaller example:
void SomeMethod<T>(IEnumerable<T> originalTypedArray, int MyId)
where T : class, IMyInterface
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this is important
{
if (originalTypedArray != null)
{
var filteredArray = originalTypedArray.Where(r => r.Ids.Contains(MyId));
// No need to cast to `IEnumerable<T>` here - we already have ensured covariance
// is valid in our generic type constraint
DoSomethingExpectingIEnumerableOfIMyInterface(filteredArray);
}
}
void DoSomethingExpectingIEnumerableOfIMyInterface(IEnumerable<IMyInterface> src)
{
foreach (var thing in src)
{
}
}
However, if you're not getting the collection as IEnumerable<T>, then the runtime is correctly failing the cast:
void SomeMethod<T>(IEnumerable<IMyInterface> originalTypedArray, int MyId)
We could give it a bunch of IEnumerable<Apple> assuming Apple : IMyInterface. Then you try to cast it to IEnumerable<T> where T = Banana and boom, code broken.

C# Covariance issue

I had a linq-to-sql generated domain entity that I cast to the proper interface like so:
public IEnumerable<IApplication> GetApplications()
{
using (var dc = new LqDev202DataContext())
{
return dc.ZApplications.Cast<IApplication>().ToList();
}
}
However I renamed the linq-to-sql table without touching my partial class and the code still compiled.
The list had the right amount of elements, but they were all null.
Do I need to write a helper method to make sure this will work, or is there a compile time safe simple built-in way to do this in .net 3.5?
You can also use this to do casting with conversions if needed:
public static IEnumerable<TDest> CastAll<TItem, TDest>(this IEnumerable<TItem> items)
{
var p = Expression.Parameter(typeof(TItem), "i");
var c = Expression.Convert(p, typeof(TDest));
var ex = Expression.Lambda<Func<TItem, TDest>>(c, p).Compile();
foreach (var item in items)
{
yield return ex(item);
}
}
From http://adventuresdotnet.blogspot.com/2010/06/better-more-type-safe-alternative-to.html

Generate a generic list of some given type

My friend is trying to create a utility function that is given some Type and in that function it creates a generic List of that type. We're having trouble creating that list:
public static List<T> GetQueryResult(string xpathQuery, Type itemType) {
// this line does not work:
List<itemType> lst = new List<itemType>();
return lst;
}
Are there any easy solutions to this?
UPDATE:
Is there any way to basically do this???
List<T> lst = new List<T>();
foreach (Sitecore.Data.Items.Item i in items) {
lst.Add(new T(i));
}
public static List<T> GetQueryResult<T>(string xpathQuery/*, Type itemType you don't need this because you specified the Type with T*/) {
// this line works now:
List<T> lst = new List<T>();
return lst;
}
Then you would call the method like so:
List<int> results = GetQueryResult<int>("xpathQuery");
Edit:
Are you wanting to do something like this?
List<YourType> lst = items.Select<Sitecore.Data.Items.Item, YourType>(
siteCoreItem => new YourType()
{
PropertyA = siteCoreItem.PropertyA,
}
);
If YourType inherrits from Sitecore.Data.Items.Item you can use Cast:
List<YourType> list = items.Cast<YourType>();
Define that method like this:
public static List<T> GetQueryResult<T>(string xpathQuery)
{
List<T> lst = new List<T>();
// do stuff
return lst;
}
and call it like this:
List<SomeType> items = SomeClass.GetQueryResult<SomeType>("query");
It is possible using reflection, for example:
var type = typeof(int); // var type = itemType : put this line to fit the method
var genericListType = typeof(List<>).MakeGenericType(type);
var genericList = Activator.CreateInstance(genericListType);
Assert.IsTrue(genericList is List<int>);
In your example, ehere do you get T from that you use in the return type? Maybe there is no need to use here reflection.
If you do not get T as generic argument then you cannot return the List as generic List and the method will have to return a non generic type (like IList instead of List).
While Elisha's answer shows you how you can create a constructed generic type from a Type instance, it's not going to help you because what I think you want to do is not possible: the signature of the GetQueryResult method is illegal because T is unspecified (unless the method is a member of a generic type itself).
The method will not compile as given.
If you already know the type, you can change it to
public static List<T> GetQueryResult<T>(string xpathQuery)
{
var lst = new List<T>();
return lst;
}
but that's probably not what you want...
Generic type arguments are resolved compile time, so to have the code working you'd need to pass itemType as a type argument or change the return type to IList And Them use the solution given by ELisha but that would mean loosing type information on the Call site
Answer to the updated question:
public List<T> GetQueryResult<T>(string xPathQuery)
{
var items = ;// logic to get items
var list = new List<T>();
foreach (Sitecore.Data.Items.Item item in items)
{
list.Add((T) Activator.CreateInstance(typeof(T), item));
}
return list;
}
I assume that T has a constructor that gets Sitecore.Data.Items.Item, if it won't have the code will fail at runtime.
There must be a safer way to do it, it'll be better if you can give wider context to the problem.
As others have demonstrated, the only way to solve your updated question for any T is with reflection. However, if T is restricted to a well known set of types that you can modify, you could do this:
public interface IItemContainer
{
void SetItem(Sitecore.Data.Items.Item item);
}
public static List<T> GetQueryResult<T>(string xpathQuery)
where T : IItemContainer, new() {
IList<Sitecore.Data.Items.Item> items = GetAListOfItemsSomehow(xpathQuery);
List<T> result = new List<T>();
foreach (Sitecore.Data.Items.Item item in items) {
T obj = new T();
obj.SetItem(item);
result.add(obj);
}
return result;
}
Any types you want to use for T would then have to implement IItemContainer.
public static List<T> GetQueryResult<T>(string xpathQuery) {
List<T> lst = new List<T>();
return lst;
}
is the only way if you want static typing. Otherwise you could do
public static IList GetQueryResults(string xpathQuery, Type itemType) {
Type tp = typeof(List<>).MakeGenericType(itemType);
IList lst = (IList)Activator.CreateInstance(tp);
return lst;
}
but using a non-generic list would probably be better in that case.
Edit: You asked another question in the same post:
The 3 ways of creating an instance of a generic type are
use the where T : new() constraint and use the default constructor (doesn't seem good enough for you).
Use reflection. Rarely the best idea.
Specify a creator function
like this:
public static List<T> GetQueryResults<T>(string xpathQuery, Func<int, T> creator) {
var result = new List<T>();
foreach (i in something)
result.add(creator(i));
return result;
}
and then invoke it like:
List<int> l = GetQueryResults("something", i => new MyObject(i));

How do I write a generic method that takes different types as parameters?

I have the following extension method to add the elements in one collection to another collection:
public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> list)
{
foreach (var item in list)
{
collection.Add(item);
}
}
This works fine if the IEnumerable list is the same type as the ICollection I'm trying to add it to. However, if I have something like this:
var animals = new List<Animal>();
var dogs = new List<Dog>(); // dog is a subclass of animal
animals.AddRange(dogs); // this line has a compiler error, it can't infer the type
How do I modify my extension method to be able to do something like this, if the type of the IEnumerable is a subclass (or implements the interface) of the T type?
This method will give you what you want:
public static void AddRange<A,B>(
this ICollection<A> collection,
IEnumerable<B> list)
where B: A
{
foreach (var item in list)
{
collection.Add(item);
}
}
A couple of notes:
The only way that this can work is if you use a type for B that is derived from A. For example, Dog is a subclass of Animal, so AddRange will work. This is enforced by the "where B: A" clause. Without this, the ICollection.Add() call will fail due to an incompatible type for B being passed into the ICollection expecting an A.
There's not a lot of need to restrict the type of A to anything in the Animal type hierarchy; the extension method could be used anywhere you have one type deriving from another.
This isn't really a problem of the compiler not being able to infer the types. Even if you explicitly passed the types for A and B everywhere, you'll still get a compiler error.
You can do this:
public static void AddRange<T,T2>(this ICollection<T> collection, IEnumerable<T2> list) where T2: T {
foreach (var item in list) {
collection.Add(item);
} }
wait for C# 4.0 or use the Linq extension method Cast:
List<Animal> animals = new List<Animal>();
List<Dog> dogs = new List<Dog>();
animals.AddRange(dogs.Cast<Animal>());
I would use constraint to restrict the type.
public static void AddRange<T, TK>(this ICollection<T> collection, IEnumerable<TK> list) where TK : T
{
foreach (var item in list)
{
collection.Add(item);
}
}
public static void AddRange<TA, TB>(this ICollection<TA> collection,
IEnumerable<TB> list) where TB : TA
(incorporated Craig Walker's comment)

Categories