C# 返回未知的通用列表<T>
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/588524/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
return unknown Generic List<T>
提问by stevenrosscampbell
and thanks for any assistance.
并感谢您的任何帮助。
How would I return from a method an unknown Generic.List type.
我将如何从一个未知的 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;
}
}
In the below example I recieve an error similar to: Can't Convert List<A>
to List<T>
在下面的示例中,我收到类似于以下内容的错误:无法转换List<A>
为List<T>
Is this possible? The error occurs on the 'return a;' line of code.
Also, What will I need to do to make sure an error does not occur on the line:
这可能吗?错误发生在“return a;”上 代码行。
另外,我需要做什么来确保线路上不会发生错误:
List<A> a= GetData("A");
Thanks, Steven
谢谢,史蒂文
采纳答案by John Rasch
Use IList
instead of List<T>
.
使用IList
代替List<T>
。
回答by Jon Erickson
EDIT per Orion's answer below, added contraint that AnthonyWJones suggested
根据下面的猎户座回答进行编辑,添加了 AnthonyWJones 建议的限制
you probably should have an interface/abstract class that A and B are inheriting from
你可能应该有一个 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;
}
回答by Orion Edwards
You can't directly return a List<T>
like this.
你不能List<T>
像这样直接返回 a 。
Why? Basically because List<A>
and List<B>
(or List<string>
vs List<int>
which is the same thing) are considered as 2 totally seperate unrelated classes.
Just as you can't return a string
from a function which is declared to return int
, you can't return a List of strings from a function which is declared to return a list of ints. The <T>
here is a bit of a red herring. You couldn't write a generic method which returned both strings and ints either...
为什么?基本上是因为List<A>
和List<B>
(或List<string>
vsList<int>
是同一件事)被认为是 2 个完全独立的不相关类。
正如您不能string
从声明为 return 的函数中返回 a 一样int
,您也不能从声明为返回整数列表的函数中返回字符串列表。在<T>
这里是一个有点红鲱鱼。你也不能编写一个返回字符串和整数的通用方法......
See herefor more info on that kind of thing.
有关此类事情的更多信息,请参见此处。
So what you have to do is return something that both types derive from (what they "have in common".)
As John Rasch says, you could return IList
, (note the NON generic, so it's just a list of object
s) or simply return it as an object
. Unfortunately there is no way to preserve the type of the list.
所以你必须做的是返回两种类型派生自的东西(它们“有共同点”。)
正如约翰拉施所说,你可以返回IList
, (注意非泛型,所以它只是一个object
s的列表)或简单地返回它作为一个object
. 不幸的是,没有办法保留列表的类型。
回答by Craig Gidney
If you don't know the type you want until run-time, then generics are probably the wrong tool for the job.
如果您直到运行时才知道您想要的类型,那么泛型可能是该工作的错误工具。
If your function significantly changes behavior (like changing return type) based on an argument, then it should probably be two functions.
如果您的函数根据参数显着更改行为(例如更改返回类型),那么它可能应该是两个函数。
It looks like this function should not be generic, and should actually be two functions.
看起来这个函数不应该是泛型的,实际上应该是两个函数。
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;
}
回答by AnthonyWJones
An alternative to being limited to returning a list of objects would be to either ensure that A and B derive from a common base type or implement a common interface, then return a list of that base type or interface. Include a constraint on the Generic method to that effect:-
限制返回对象列表的另一种方法是确保 A 和 B 从公共基类型派生或实现公共接口,然后返回该基类型或接口的列表。包括对 Generic 方法的约束,以实现该效果:-
List<ICommon> GetData<T>() where T: ICommon
{
}
回答by Kevin Kibler
Unless there's a specific reason that you can't specify the actual type ahead of time, you can just make the method itself generic:
除非有特定原因不能提前指定实际类型,否则您可以使方法本身通用:
public void Main() {
List<A> a = GetData<A>();
}
public List<TType> GetData<TType>() {
List<TType> list= new List<TType>();
...
return list;
}
回答by Richard Berg
I had to solve a similar problem recently where none of the proposed solutions was satisfactory; constraining the type parameter was not practical. Instead, I let the consumers of the method decide how to munge the data. For example, you can write a generic version of String.Split() that returns a strongly typed List, so long as you tell it how to convert substrings into T's.
我最近不得不解决一个类似的问题,其中提出的解决方案都不令人满意;限制类型参数是不切实际的。相反,我让方法的使用者决定如何处理数据。例如,您可以编写返回强类型列表的 String.Split() 的通用版本,只要您告诉它如何将子字符串转换为 T 即可。
Once you are willing to shift responsibility up the call stack (and get comfortable passing lambdas around), you can generalize this pattern arbitrarily. For instance, if the way you GetData() varies (as some responses apparently assume), you can hoist that function into the caller's scope as well.
一旦您愿意将责任转移到调用堆栈上(并且可以轻松地传递 lambda 表达式),您就可以任意概括这种模式。例如,如果 GetData() 的方式不同(正如某些响应显然假设的那样),您也可以将该函数提升到调用者的作用域中。
Demo:
演示:
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);
}
Functional programming gurus will probably yawn at this exploration: "you're just composing Map a few times." Even C++ guys might claim it's an example where template techniques (i.e. STL transform() + functors) require less work than generics. But as someone who primarily does C# it was nice to find a solution that preserved both type safety and idiomatic language usage.
函数式编程大师可能会在这种探索中打哈欠:“你只是编写了几次 Map。” 甚至 C++ 人也可能声称这是一个模板技术(即 STL 转换()+ 函子)比泛型需要更少工作的例子。但作为主要使用 C# 的人,很高兴找到一种既保留类型安全又保留惯用语言使用的解决方案。
回答by hangar18
I know its way too late but I came here with the same issue and this is how i worked it out using interfaces. Thought I'll post it for the benefit of others
我知道它来得太晚了,但我带着同样的问题来到这里,这就是我使用接口解决的方法。以为我会发布它以造福他人
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();
}
}
}
Similarly for Entity1.
Entity1 也是如此。
Now in my class (my business layer) I have this method
现在在我的班级(我的业务层)中,我有这个方法
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;
}
From the UI, I would call it like this
从用户界面,我会这样称呼它
BusinessClass b = new BusinessClass();
List<IEntity> a = b.GetEntities(Common.EntityType.Accounts);
Hope this helps
希望这可以帮助
回答by Orace
A solution is to encapsulate the data in a container that will work as a clientin the Visitor Pattern.
一种解决方案是封装在容器中,将工作作为所述数据客户端在访问者模式。
First some interface matching the pattern:
首先是一些匹配模式的接口:
/// <summary>
/// The Client
/// </summary>
interface IDataContainer
{
void AcceptDataProcessor(IDataProcessor dataProcessor);
}
/// <summary>
/// The Visitor.
/// </summary>
interface IDataProcessor
{
void WorkOn<TData>(List<TData> data);
}
Then an implementation of each:
然后每个的实现:
class DataContainer<TData> : IDataContainer
{
readonly List<TData> list;
public DataContainer(List<TData> list)
{
this.list = list;
}
public void AcceptDataProcessor(IDataProcessor dataProcessor)
{
dataProcessor.WorkOn(list); // Here the type is known.
}
}
class PrintDataProcessor : IDataProcessor
{
public void WorkOn<TData>(List<TData> data)
{
// print typed data.
}
}
Then a use of it:
然后使用它:
public void Main()
{
var aContainer = GetData("A");
var bContainer = GetData("B");
var printProccessor = new PrintDataProcessor();
aContainer.AcceptDataProcessor(printProccessor); // Will print A data
bContainer.AcceptDataProcessor(printProccessor); // Will print B data
}
public IDataContainer GetData(string listType)
{
if (listType == "A")
return new DataContainer<A>(new List<A>());
if (listType == "B")
return new DataContainer<B>(new List<B>());
throw new InvalidOperationException();
}
The idea is that DataContainer
know the underlying type but does'nt expose it.
这个想法是DataContainer
知道底层类型但不公开它。
- It doesn't expose it so
GetData
can contains any kind of data (but it's hided). DataContainer
know the underlying type, it is in charge to call the good typed method of the worker:dataProcessor.WorkOn(list);
- 它不会公开它,因此
GetData
可以包含任何类型的数据(但它是隐藏的)。 DataContainer
知道底层类型,负责调用worker的好类型方法:dataProcessor.WorkOn(list);
It's a powerful pattern but cost a lot in terms of code.
这是一个强大的模式,但在代码方面成本很高。
回答by Marc Selman
You could do something like:
你可以这样做:
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();
}
}
Maybe you can modify that to your needs.
也许你可以根据你的需要修改它。