返回未知的通用列表<t> </t>

时间:2009-02-26 00:11:59

标签: c# generics types

并感谢您的帮助。

如何从未知Generic.List类型的方法返回。

public void Main()
{
  List<A> a= GetData("A");   
}

public List<T> GetData(string listType)
{
   if(listType == "A")
   {
     List<A> a= new List<A>() 
     ...
     return a; 
   }
   else
   {
     List<B> b = new List<B>()
     return b;

   }
}

在下面的示例中,我收到类似于以下内容的错误:无法将List<A>转换为List<T>

这可能吗?错误发生在'return a;'上代码行。
另外,我需要做些什么来确保线路上不会发生错误:

List<A> a= GetData("A");   

谢谢, 史蒂文

9 个答案:

答案 0 :(得分:23)

使用IList代替List<T>

答案 1 :(得分:13)

仅限于返回对象列表的替代方法是确保A和B派生自公共基类型或实现公共接口,然后返回该基类型或接口的列表。在Generic方法中包含一个约束: -

List<ICommon> GetData<T>() where T: ICommon
{

}

答案 2 :(得分:9)

您无法直接返回此类List<T>

为什么呢?基本上因为List<A>List<B>(或List<string> vs List<int>这是相同的事情)被视为2个完全独立的无关类。 正如您无法从声明返回string的函数返回int一样,您无法从声明返回整数列表的函数返回字符串列表。 <T>这里有点红鲱鱼。你不能写一个返回字符串和整数的通用方法......

有关此类事情的详情,请参阅here

所以你需要做的就是返回两种类型的东西(它们“有共同之处”。) 作为John Rasch says,您可以返回IList,(请注意非通用,因此它只是object s的列表)或只是将其作为object返回。不幸的是,没有办法保留列表的类型。

答案 3 :(得分:7)

除非有特定原因导致您无法提前指定实际类型,否则您可以将方法本身设为通用:

public void Main() {
    List<A> a = GetData<A>();
}

public List<TType> GetData<TType>() {
     List<TType> list= new List<TType>();
     ...
     return list; 
}

答案 4 :(得分:5)

编辑每个猎户座的答案如下,加上AnthonyWJones暗示的禁令

你可能应该有一个接口/抽象类,A和B继承自

    public interface IMyInterface { }
    public class A : IMyInterface { }
    public class B : IMyInterface { }

    public List<IMyInterface> GetData<T>() where T : IMyInterface
    {
        List<IMyInterface> myList = new List<IMyInterface>();
        if (typeof(T) == typeof(A))
        {
            myList.Add(new A());
        }
        if (typeof(T) == typeof(B))
        {
            myList.Add(new B());
        }
        return myList;
    }

答案 5 :(得分:2)

最近我不得不解决类似的问题,所提议的解决方案都不令人满意;约束类型参数是不实际的。相反,我让方法的消费者决定如何挖掘数据。例如,您可以编写一个返回强类型List的String.Split()的泛型版本,只要您告诉它如何将子串转换为T。

一旦你愿意将责任转移到调用堆栈(并且可以轻松地传递lambda),你可以任意概括这个模式。例如,如果GetData()的方式不同(正如某些响应显然所假设的那样),您也可以将该函数提升到调用者的范围内。

演示:

static void Main(string[] args)
{
    var parseMe = "Hello world!  1, 2, 3, DEADBEEF";

    // Don't need to write a fully generic Process() method just to parse strings -- you could 
    // combine the Split & Convert into one method and eliminate 2/3 of the type parameters
    List<string> sentences = parseMe.Split('!', str => str);
    List<int> numbers = sentences[1].Split(',', str => Int32.Parse(str, NumberStyles.AllowHexSpecifier | NumberStyles.AllowLeadingWhite));

    // Something a little more interesting
    var lettersPerSentence = Process(sentences,
                                     sList => from s in sList select s.ToCharArray(),
                                     chars => chars.Count(c => Char.IsLetter(c)));
}

static List<T> Split<T>(this string str, char separator, Func<string, T> Convert)
{       
    return Process(str, s => s.Split(separator), Convert).ToList();
}

static IEnumerable<TOutput> Process<TInput, TData, TOutput>(TInput input, Func<TInput, IEnumerable<TData>> GetData, Func<TData, TOutput> Convert)
{
    return from datum in GetData(input)
           select Convert(datum);
}

函数式编程大师可能会对这种探索持怀疑态度:“你只是在编写Map几次。”即使是C ++人也可能会声称这是一个模板技术(即STL transform()+ functor)需要的工作量少于泛型的例子。但作为主要使用C#的人,很高兴找到一种既能保护类型安全又能保留惯用语言的解决方案。

答案 6 :(得分:2)

您可以执行以下操作:

public void Main()
{
    List<int> a = GetData<int>();
    List<string> b = GetData<string>();
}

public List<T> GetData<T>()
{
    var type = typeof(T);
    if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
    {
        type = type.GenericTypeArguments[0];
    }

    if (type == typeof(int))
    {
        var a = new List<int> { 1, 2, 3 };
        return a.Select(v => v != null ? (T)Convert.ChangeType(v, type) : default(T)).ToList();
    }
    else if (type == typeof(string))
    {
        var b = new List<string> { "a", "b", "c" };
        return b.Select(v => v != null ? (T)Convert.ChangeType(v, type) : default(T)).ToList();
    }
}

也许您可以根据需要进行修改。

答案 7 :(得分:1)

如果你在运行时之前不知道你想要的类型,那么泛型可能是错误的工具。

如果你的函数根据参数显着改变行为(比如改变返回类型),那么它应该是两个函数。

看起来这个函数不应该是通用的,实际上应该是两个函数。

public void Main() {
    List<A> a = GetDataA();
}

public List<A> GetDataA() {
     List<A> a= new List<A>() 
     ...
     return a; 
}
public List<B> GetDataB() {
     List<B> b= new List<B>() 
     ...
     return b; 
}

答案 8 :(得分:0)

我知道它的方式为时已晚,但我带着同样的问题来到这里,这就是我使用接口的方法。以为我会发布它以造福他人

 public interface IEntity
    {
        int ID
        {
            get;
            set;
        }
    }

public class Entity2:IEntity
    {
        public string Property2;

        public int ID
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }
    }

与Entity1类似。

现在我的班级(我的业务层)我有这个方法

 public List<IEntity> GetEntities(Common.EntityType entityType)
           {
               List<IEntity> entities = new List<IEntity>();

               switch (entityType)
               {
                   case Common.EntityType.Accounts:
                       Entity1 entity1 = new Entity1();
                       entity1.Property1 = "AA";
                       entities.Add(entity1);

                       break;
                   case Common.EntityType.Brands:
                       Entity2 entity2 = new Entity2();
                       entity2.Property2 = "AA";
                       entities.Add(entity2);

                       break;
                   default:
                       break;
               }

 return entities;
       }

在UI中,我会这样称呼它

BusinessClass b = new BusinessClass();
        List<IEntity> a = b.GetEntities(Common.EntityType.Accounts);

希望这有帮助