简体   繁体   中英

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM