C# generic method can't create List<T> - c#

I have one Interface and two classes.
public interface IMyInterface { }
public class A : IMyInterface { }
public class B : IMyInterface { }
I have a generic method,
private List<T> GetList<T>(DataTable table)
where T : class, IMyInterface
{
...
}
which should return a object-list based on data in DataTable. So, I create a list in this method which I want to return at the end. I thought I could do the following,
private List<T> GetList<T>(DataTable table)
where T : class, IMyInterface
{
List<T> myList = new List<T>;
// Now I thought I could easily add Objects based on T because,
// both classes implement the interface
if (typeof(T) == typeof(B))
{
myList.Add(new B());
}
else
{
myList.Add(new A());
}
return myList;
}
But the compiler tells me that "Argument type A (B) is not assigneable"! Why is it not assignable?
Ok, alternatively can I do the following,
private List<T> GetList<T>(DataTable table)
where T : class, IMyInterface
{
List<IMyInterface> myList = new List<IMyInterface>;
// Now I can assign the Object's :)
if (typeof(T) == typeof(B))
{
myList.Add(new B());
}
else
{
myList.Add(new A());
}
return myList as List<T>;
}
The compiler didn't complain but the result of the return clause is always null. For sure there are values in myList. The cast seems to fail. Someone please help me to solve this problem more elegantly.

One way is to add new() constraint. Limitation is that you need a public parameterless constructor for the type argument T.
private static List<T> GetList<T>(DataTable table) where T : class, IMyInterface, new()
{
List<T> myList = new List<T>();
T instance = new T();
//access anything defined in `IMyInterface` here
myList.Add(instance);
return myList;
}

I don't understand what you're trying to do here. Why do you even need generics?
You were going the right way initially, deriving your types from the same interface, so make use of it. Declare your list List<IMyInterface> and simply add your objects as they are.
If later you actually need a physical representation of an enumerable with concrete A or B types, you have OfType<>() and Cast<>() for it, though it shows your polymorphism was done wrong in the first place.

Add a new constraint
private List<T> GetList<T>(DataTable table) where T : class, IMyInterface, new()
{
return new List<T>(){ new T() };
}

You should cast object, before add it to List:
private static List<T> GetList<T>(DataTable table) where T : class, MyInterface
{
List<T> myList = new List<T>();
//Now i thought i can easily add Objects based on T, because both classes
//implement the interface
if (typeof (T) == typeof (B))
{
// use of 'as' operator
myList.Add(new B() as T);
}
else
{
myList.Add(new A() as T);
}
return myList;
}
But anyway I am not getting a point, what your are trying to achieve.
Also myList as List<T> will surely result as null, since you cannot cast generic collection with as operator as List<T> is not declared as Covariant. You should explicitly call .Cast<T>() method to create new collection.

I'm guessing you actually want to do something like,
public IList<T> GetList<T>(
DataTable table,
Func<DataRow, T> extractor)
{
var result = new T[table.Rows.Count];
for (var i = 0; i < table.Rows.Count; i++)
{
result[i] = extractor(table.Rows[i]);
}
return result;
}
extractor being the delegate for converting a DataRow to T.
this would work more simply than you might expect,
// referencing System.Data.DataSetExtensions
var list = GetList(
data,
row => new A
{
Id = row.Field<int>("id"),
...
});
list would be an IList<A> among other types.

Try this:
private List<T> GetList<T>(DataTable table) where T : class, IMyInterface {
List<T> myList = new List<T>();
if (typeof(T) == typeof(B)) {
myList.Add((T)new B());
}
else {
myList.Add((T)new A());
}
return myList
}

Right only for class A and B
static List<T> GetList<T>(DataTable table) where T : class, IMyInterface
{
List<T> myList = new List<T>();
IMyInterface obj;
if (typeof(T) == typeof(B))
{
obj = new B();
}
else
{
obj = new A();
}
myList.Add((T)obj);
return myList;
}

Related

Assign List to a generic list

How would you assign a list to a generic list since they are not the same type.
If I have a generic list:
List<T> myList = new List<T>();
and I have another list
List<OtherType> otherList = new List<OtherType>();
After I fill otherList with values. What are ways I can assign otherList to the generic list? Preferably without using a foreach.
if they are the same type you can do a basic type conversion
if(typeof(T) == typeof(OtherType))
myList = otherList as List<T>;
But that would make no sense, so I'd imagine you need some kind of conversion, problem is we need to specify that T is assignable from your base class
public static class StaticFoo
{
public static List<T> Foo<T>() where T : class
{
List<MyOtherClass> returnList = new List<MyOtherClass>() { new MyOtherClass() };
if(typeof(T).IsAssignableFrom(typeof(MyOtherClass)))
return returnList.Select(x => x as T).ToList();
throw new Exception($"Cannot convert {typeof(T)} to MyOtherClass");
}
}
public class MyClass { }
public class MyOtherClass : MyClass { }
The above code will work if you call it with T = MyClass or any other class that myOtherClass can be cast to. Alternatively you might want a concrete conversion method for a set of predefined types, it's kind of hacky but you could do something like this
public static class StaticFoo
{
public static List<T> Foo<T>() where T : class
{
List<MyOtherClass> returnList = new List<MyOtherClass>() { new MyOtherClass() };
return returnList.Select(x => x.Convert(typeof(T)) as T).ToList();
}
}
public class MyOtherClass {
public object Convert(Type type) {
if (type == typeof(string)) //more if statements for more types
return this.ToString(); //just an example
throw new NotImplementedException($"No cast available for type {type}");
}
}
Some context for the relationship between the generic type and your concrete class would be helpful
edit:
some advice that ignores your actual question. Most likely, you want to create an interface and return a list of that interface (I'm assuming that will match your use case more closely). Alternatively just change the signature to return List< object> - then you can do
return otherList.ToList<object>();
List<T> is invariant, so you can only assign lists of the same type. The closest you can come is creating a new list with the same items.
List<T> list = otherList.Select( x => (T)x ).ToList();

How do I implement IEnumerable<T>

I know how to implement the non generic IEnumerable, like this:
using System;
using System.Collections;
namespace ConsoleApplication33
{
class Program
{
static void Main(string[] args)
{
MyObjects myObjects = new MyObjects();
myObjects[0] = new MyObject() { Foo = "Hello", Bar = 1 };
myObjects[1] = new MyObject() { Foo = "World", Bar = 2 };
foreach (MyObject x in myObjects)
{
Console.WriteLine(x.Foo);
Console.WriteLine(x.Bar);
}
Console.ReadLine();
}
}
class MyObject
{
public string Foo { get; set; }
public int Bar { get; set; }
}
class MyObjects : IEnumerable
{
ArrayList mylist = new ArrayList();
public MyObject this[int index]
{
get { return (MyObject)mylist[index]; }
set { mylist.Insert(index, value); }
}
IEnumerator IEnumerable.GetEnumerator()
{
return mylist.GetEnumerator();
}
}
}
However I also notice that IEnumerable has a generic version, IEnumerable<T>, but I can't figure out how to implement it.
If I add using System.Collections.Generic; to my using directives, and then change:
class MyObjects : IEnumerable
to:
class MyObjects : IEnumerable<MyObject>
And then right click on IEnumerable<MyObject> and select Implement Interface => Implement Interface, Visual Studio helpfully adds the following block of code:
IEnumerator<MyObject> IEnumerable<MyObject>.GetEnumerator()
{
throw new NotImplementedException();
}
Returning the non generic IEnumerable object from the GetEnumerator(); method doesn't work this time, so what do I put here? The CLI now ignores the non generic implementation and heads straight for the generic version when it tries to enumerate through my array during the foreach loop.
If you choose to use a generic collection, such as List<MyObject> instead of ArrayList, you'll find that the List<MyObject> will provide both generic and non-generic enumerators that you can use.
using System.Collections;
class MyObjects : IEnumerable<MyObject>
{
List<MyObject> mylist = new List<MyObject>();
public MyObject this[int index]
{
get { return mylist[index]; }
set { mylist.Insert(index, value); }
}
public IEnumerator<MyObject> GetEnumerator()
{
return mylist.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
You probably do not want an explicit implementation of IEnumerable<T> (which is what you've shown).
The usual pattern is to use IEnumerable<T>'s GetEnumerator in the explicit implementation of IEnumerable:
class FooCollection : IEnumerable<Foo>, IEnumerable
{
SomeCollection<Foo> foos;
// Explicit for IEnumerable because weakly typed collections are Bad
System.Collections.IEnumerator IEnumerable.GetEnumerator()
{
// uses the strongly typed IEnumerable<T> implementation
return this.GetEnumerator();
}
// Normal implementation for IEnumerable<T>
IEnumerator<Foo> GetEnumerator()
{
foreach (Foo foo in this.foos)
{
yield return foo;
//nb: if SomeCollection is not strongly-typed use a cast:
// yield return (Foo)foo;
// Or better yet, switch to an internal collection which is
// strongly-typed. Such as List<T> or T[], your choice.
}
// or, as pointed out: return this.foos.GetEnumerator();
}
}
Why do you do it manually? yield return automates the entire process of handling iterators. (I also wrote about it on my blog, including a look at the compiler generated code).
If you really want to do it yourself, you have to return a generic enumerator too. You won't be able to use an ArrayList any more since that's non-generic. Change it to a List<MyObject> instead. That of course assumes that you only have objects of type MyObject (or derived types) in your collection.
If you work with generics, use List instead of ArrayList. The List has exactly the GetEnumerator method you need.
List<MyObject> myList = new List<MyObject>();
make mylist into a List<MyObject>, is one option
Note that the IEnumerable<T> allready implemented by the System.Collections so another approach is to derive your MyObjects class from System.Collections as a base class (documentation):
System.Collections: Provides the base class for a generic collection.
We can later make our own implemenation to override the virtual System.Collections methods to provide custom behavior (only for ClearItems, InsertItem, RemoveItem, and SetItem along with Equals, GetHashCode, and ToString from Object). Unlike the List<T> which is not designed to be easily extensible.
Example:
public class FooCollection : System.Collections<Foo>
{
//...
protected override void InsertItem(int index, Foo newItem)
{
base.InsertItem(index, newItem);
Console.Write("An item was successfully inserted to MyCollection!");
}
}
public static void Main()
{
FooCollection fooCollection = new FooCollection();
fooCollection.Add(new Foo()); //OUTPUT: An item was successfully inserted to FooCollection!
}
Please note that driving from collection recommended only in case when custom collection behavior is needed, which is rarely happens. see usage.

Generic method with variance in C#?

Consider following classes (inheritance tree):
public class A {}
public class B: A {}
And this method:
public IList<A> MyMethod(){
IList<B> result = new List<B>();
//add some items to result
return result;
}
The compiler is unhappy. Error is Cannot convert expression type IList<B> to return type IList<A>. How do I solve this ? In another words how to specify that MyMethod will return IList<T> of T where T can be anything that inherits from A or instances of A itself ?
What you're asking for is impossible because IList<T> does not support variance -- you cannot use IList<B> anywhere that is expecting IList<A>. You'll have to explain more details of what you want in order to come up with a solution.
Possible solutions are:
public IList<A> MyMethod(){
IList<A> result = new List<A>();
//add some items to result
return result;
}
Or
public IEnumerable<A> MyMethod(){
IList<B> result = new List<B>();
//add some items to result
return result;
}
You cannot convert an IList<B> to IList<A>, even if B inherits from A. Otherwise, the user might attempt to add an instance of A that is not B into the list.
public void Example(){
IList<B> listB = new List<B>();
IList<A> listA = listB;
listA.Add(new A()); // Can't insert A into a list of B
}
Can you return IEnumerable<A> instead of IList<A>? IEnumerable<A> is covariant, unlike IList<A>.
how to specify that MyMethod will return IList of T where T can be anything that inherits from A or instances of A itself ?
You don't have to
You can just declare that it returns IList<A>. Why? Becase - given that B inherits from A - every item of B can be passed where the requiered type is A.
Call it polymorphism by inheritance, Liskov substitution principle, or method variance, the name doesn't matter. What matters is that the following works (tested on LinqPad):
public class A {}
public class B: A {}
public IList<A> MyMethod()
{
var result = new List<A>();
//add some items to result
result.Add(new B());
return result;
}
Genetic alternatives
In fact, you can tell that you are going to return a IList<TA> and request a few derived types (TB, TC...) to populate it with. That's right, the following example also works (tested on LinqPad):
void Main()
{
MyMethod<A, B, C>();
}
public class A {}
public class B: A {}
public class C: A {}
public IList<TA> MyMethod<TA, TB, TC>()
where TB : TA, new()
where TC : TA, new()
where TA : class
{
var result = new List<TA>();
//add some items to result
result.Add(new B() as TA);
result.Add(new C() as TA);
return result;
}
Or if you want to keep a particular base type (say you want to return an IList<A> but it actually contains items of classes that derive from A, then you can do this:
void Main()
{
MyMethod<B, C>();
}
public class A {}
public class B: A {}
public class C: A {}
public IList<A> MyMethod<T1, T2>()
where T1 : A, new()
where T2 : A, new()
{
var result = new List<A>();
//add some items to result
result.Add(new T1() as A);
result.Add(new T2() as A);
return result;
}
You don't have to, but you can
OK, if you really want to say it returns IList<T> where T : A. Then say that!
void Main()
{
MyMethod<B>();
}
public class A {}
public class B: A {}
//public class C: A {} //Even if I don't add that class
public IList<T> MyMethod<T>()
where T : A, new()
{
var result = new List<T>();
//add some items to result
result.Add(new T());
return result;
}
Yes, that one cannot return a mix of item of type T and items of type A, because it says it returns IList<T> and not every item of type A is also an item of type T.
What happens with your code
Look at your code:
public IList<A> MyMethod(){
IList<B> result = new List<B>();
//add some items to result
return result;
}
You are trying to return an IList<B> when you said that you was going to return an IList<A>. Let's suppose that that works... then what would happen to the caller of your method? Let's see:
public class A {}
public class B: A {}
public class C: A {}
void Main()
{
//Hmmm... I need a IList<T>, let's call MyMethod!
IList<A> list = MyMethod();
//Cool, I got an IList<A>, now let's add some items...
var item = new C();
//Well, item is of type C...
// and C inherits from A, so I must be able to add it...
list.Add(item); //BOOM!
//It was actually an IList<B>!
// and C doesn't dervive from B, so you can't add it.
}
DFTBA!

Having trouble with Generics in this .NET code

i'm trying to make a mixed collection of Types. I know the types at the start.. but I can't seem to figure out the syntax to make the collection, etc.
eg.
....
// I leave the typo there, for embarrassment :(
Initialize(new []{ typeof(Cat), typeof(Dog), typeof(JohnSkeet) });
...
public Foo Initialize(IEnumerable<Type> types)
{
// for each type, set up the inmemory storage.
foreach(var type in types)
{
// ????
// Create an empty list, which will only contain this 'type'
// I'm guessing, an IDictionary<type, ICollection<type>>().. thingy ?
}
}
public ICollection<Type> SomeTypeData(Type type)
{
// Return the collection, for this type.
}
Does this mane sense? Is this possible?
Okay, now that I think I know what you want, it would look something like this:
// This can't really be *properly* statically typed
private readonly Dictionary<Type, object> typeMap = new
Dictionary<Type, object>();
public Foo Initialize(IEnumerable<Type> types)
{
Type genericListType = typeof(List<>);
foreach(var type in types)
{
// MakeGenericType is really badly named
Type constructedListType = genericListType.MakeGenericType(type);
typeMap[type] = Activator.CreateInstance(constructedListType);
}
}
// We can't express this particularly safely either,
// although we *could* return the non-generic IList
public object SomeTypeData(Type type)
{
return typeMap[type];
}
// This *is* statically typed, although we need to cast inside
public IList<T> SomeTypeData<T>()
{
return (IList<T>) typeMap[typeof(T)];
}
See this blog post for a similar example.
Note that basically you're trying to represent something which generics simply can't handle, in terms of the internal dictionary type... and the first form of SomeTypeData can't be statically typed either... because that means knowing the type at compile time when we'll only actually be given it at execution time.
It looks to me like you're trying to create some kind of instance repository; a class that stores a list of instances of a given type.
Here's an example implementation. I've included both a generic and non-generic version of the SomeTypeData method:
public class InstanceRepository
{
private IDictionary<Type, ICollection> _Instances = new Dictionary<Type, ICollection>();
public ICollection SomeTypeData(Type type)
{
ICollection instanceList;
if (!_Instances.TryGetValue(type, out instanceList))
{
// this type does not exist in our dictionary, so let's create a new empty list
// we could do this:
//instanceList = new List<object>();
// but let's use reflection to make a more type-specific List<T> instance:
instanceList = (ICollection)Activator.CreateInstance(typeof(List<>).MakeGenericType(type));
// now add it to the dictionary
_Instances.Add(type, instanceList);
}
// Return the collection, for this type.
return instanceList;
}
public IList<T> SomeTypeData<T>()
{
Type type = typeof(T);
ICollection instanceList;
if (!_Instances.TryGetValue(typeof(T), out instanceList))
{
instanceList = new List<T>();
_Instances.Add(type, instanceList);
}
// here we are assuming that all of the lists in our dictionary implement IList<T>.
// This is a pretty safe assumption, since the dictionary is private and we know that
// this class always creates List<T> objects to put into the dictionary.
return (IList<T>)instanceList;
}
}
Below is a usage example:
Generic:
InstanceRepository repository = new InstanceRepository();
var listOfCats = repository.SomeTypeData<Cat>();
listOfCats.Add(new Cat());
Cat firstCat = listOfCats[0];
Console.WriteLine(listOfCats.GetType().FullName);
Non-Generic:
InstanceRepository repository = new InstanceRepository();
var listOfCats = (IList<Cat>)repository.SomeTypeData(typeof(Cat));
listOfCats.Add(new Cat());
Cat firstCat = listOfCats[0];
Console.WriteLine(listOfCats.GetType().FullName);
I guess you want something like
_dict[typeof(Cat)]=new List<Cat>();
_dict[typeof(Dog)]=new List<Dog>();
only programatically based on given types?
Something like this should work:
public void Initialize(IEnumerable<Type> types)
{
foreach(var type in types)
{
var list = Activator.CreateInstance(Type.GetType("System.Collections.Generic.List`1").MakeGenericType(type));
_cache[type] = list;
}
}
public ICollection<T> Get<T>()
{
object list;
if (_cache.TryGetValue(typeof(T), out list)
{
return list as ICollection<T>;
}
else
{
...
}
}
var cats = Get<Cat>();
I'm not sure I fully understand you're question, but if you already have an IEnumerable<Type> which contains an enumeration of Type objects, then why not just use that to initialize some type of Collection (such as List<Type>)?
public ICollection<Type> Initialize(IEnumerable<Type> types)
{
ICollection<Type> collection = new List<Type>(types);
return collection;
}

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));

Categories