C#:声明和使用具有不同类型的泛型类列表,如何?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/590540/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 09:26:13  来源:igfitidea点击:

C#: Declaring and using a list of generic classes with different types, how?

c#.netgenericslist

提问by Stécy

Having the following generic class that would contain either string, int, float, longas the type:

具有以下泛型类,其中包含以下任string, int, float, long一类型:

public class MyData<T>
{
    private T _data;

    public MyData (T value)
    {
        _data = value;
    }

    public T Data { get { return _data; } }
}

I am trying to get a list of MyData<T>where each item would be of different T.

我正在尝试获取MyData<T>每个项目不同之处的列表T

I want to be able to access an item from the list and get its value as in the following code:

我希望能够访问列表中的项目并获取其值,如下面的代码所示:

MyData<> myData = _myList[0];    // Could be <string>, <int>, ...
SomeMethod (myData.Data);

where SomeMethod()is declared as follows:

其中SomeMethod()声明如下:

public void SomeMethod (string value);
public void SomeMethod (int value);
public void SomeMethod (float value);


UPDATE:

更新:

SomeMethod()is from another tier class I do not have control of and SomeMethod(object)does not exist.

SomeMethod()来自我无法控制SomeMethod(object)且不存在的另一个层级。



However, I can't seem to find a way to make the compiler happy.

但是,我似乎找不到让编译器满意的方法。

Any suggestions?

有什么建议?

Thank you.

谢谢你。

采纳答案by Leon van der Walt

Delegates can really help simplify this, and still keep things type-safe:

委托确实可以帮助简化这一点,并且仍然保持类型安全:

public void TestMethod1()
{
    Action<SomeClass, int> intInvoke = (o, data) => o.SomeMethod(data);
    Action<SomeClass, string> stringInvoke = (o, data) => o.SomeMethod(data);

    var list = new List<MyData> 
    {
        new MyData<int> { Data = 10, OnTypedInvoke = intInvoke },
        new MyData<string> { Data = "abc", OnTypedInvoke = stringInvoke }
    };

    var someClass = new SomeClass();
    foreach (var item in list)
    {
        item.OnInvoke(someClass);
    }
}

public abstract class MyData
{
    public Action<SomeClass> OnInvoke;
}

public class MyData<T> : MyData
{
    public T Data { get; set; }
    public Action<SomeClass, T> OnTypedInvoke 
    { set { OnInvoke = (o) => { value(o, Data); }; } }
}

public class SomeClass
{
    public void SomeMethod(string data)
    {
        Console.WriteLine("string: {0}", data);
    }

    public void SomeMethod(int data)
    {
        Console.WriteLine("int: {0}", data);
    }
}

回答by Mehrdad Afshari

Inherit MyData<T>from a non-generic MyDataclass and make a list of that.

MyData<T>从非泛型MyData类继承并列出该类。

This way, you can't automatically resolve the overload. You have to do it manually.

这样,您就无法自动解决过载问题。你必须手动完成。

abstract class MyData { 
   protected abstract object GetData();
   protected abstract Type GetDataType(); 
   public object Data {
      get { return GetData(); } 
   }
   public Type DataType {
      get { return GetDataType(); } 
   }
}

class MyData<T> : MyData { 
   protected override object GetData() { return Data; }
   protected override Type GetDataType() { return typeof(T); }
   public new T Data {
     get { ... }
   }
}

回答by AnthonyWJones

In that case you need MyData<object>since that is the only thing those types have in common.

在那种情况下,您需要,MyData<object>因为这是这些类型唯一的共同点。

回答by Mendelt

Generics allow you to specify one type for the whole list when you create the list, for example a list for storing int would be created like this

泛型允许您在创建列表时为整个列表指定一种类型,例如,将像这样创建一个用于存储 int 的列表

var myData = new MyData<int>();

If you want to store multiple types in the same generic list you can specify a common base type or interface for those types. Unfortunately in your case the only common base type for the types you want to store would be object.

如果你想在同一个泛型列表中存储多个类型,你可以为这些类型指定一个公共基类型或接口。不幸的是,在您的情况下,您要存储的类型的唯一通用基类型是 object。

var myData = new MyData<object>();

But you can just use the non-generic list for storing objects.

但是您可以只使用非通用列表来存储对象。

回答by Amy B

Just use an ArrayList and forget the MyData<T>type.

只需使用 ArrayList 并忘记MyData<T>类型。

ArrayList myStuff = getStuff();
float x = myStuff.OfType<float>().First();
SomeMethod(x);
string s = myStuff.OfType<string>().First();
SomeMethod(s);

The problem with MyData<T>is that you're expecting the compiler to check a type that is only known at runtime. Compilers check types that are known at compile time.

问题MyData<T>在于您希望编译器检查仅在运行时已知的类型。编译器检查编译时已知的类型。

回答by Franci Penov

You can't do it the way you want.

你不能按照你想要的方式去做。

When an instance of a generic class is initialized, it is bound to particular type. Since you want to hold objects of different types in your list, you have to create an instance bound to the least common denominator — in your case it's Object.

初始化泛型类的实例时,它会绑定到特定类型。由于您想在列表中保存不同类型的对象,您必须创建一个绑定到最小公分母的实例——在您的情况下它是 Object。

However, that means that Data property now will return an object of type Object. The compiler cannot infer the actual data type at compile time, so it can choose the appropriate SomeMethodoverload.

但是,这意味着 Data 属性现在将返回一个 Object 类型的对象。编译器在编译时无法推断出实际的数据类型,因此它可以选择适当的SomeMethod重载。

You have to either provide an overload of SomeMethodthat takes Object as a parameter, or remove the requirement to hold different such different types in your collection.

您必须提供一个SomeMethod将 Object 作为参数的重载,或者取消在您的集合中保存不同类型的要求。

Or you can go with a standard IEnumerablecollection (like Array) and use the OfType<>extension method to get the subset of the collection of particular type.

或者您可以使用标准IEnumerable集合(如 Array)并使用OfType<>扩展方法来获取特定类型集合的子集。

回答by Kent Boogaart

Suggested wildcards a while back here. Closed as "won't fix" :(

此处建议使用通配符。关闭为“不会修复”:(

回答by Joseph

I think the issue that you're having is because you're trying to create a generic type, and then create a list of that generic type. You could accomplish what you're trying to do by contracting out the data types you're trying to support, say as an IData element, and then create your MyData generic with a constraint of IData. The downside to this would be that you would have to create your own data types to represent all the primitive data types you're using (string, int, float, long). It might look something like this:

我认为您遇到的问题是因为您正在尝试创建一个泛型类型,然后创建该泛型类型的列表。您可以通过将您尝试支持的数据类型(例如 IData 元素)外包,然后创建具有 IData 约束的 MyData 泛型来完成您想要做的事情。这样做的缺点是您必须创建自己的数据类型来表示您使用的所有原始数据类型(字符串、整数、浮点数、长整数)。它可能看起来像这样:

public class MyData<T, C>
    where T : IData<C>
{
    public T Data { get; private set; }

    public MyData (T value)
    {
         Data = value;
    }
}

public interface IData<T>
{
    T Data { get; set; }
    void SomeMethod();
}

//you'll need one of these for each data type you wish to support
public class MyString: IData<string>
{
   public MyString(String value)
   {
       Data = value;
   }

   public void SomeMethod()
   {
       //code here that uses _data...
       Console.WriteLine(Data);
   }

   public string Data { get; set; }
}

and then you're implementation would be something like:

然后你的实现将是这样的:

var myData = new MyData<MyString, string>(new MyString("new string"));    
// Could be MyString, MyInt, ...
myData.Data.SomeMethod();

it's a little more work but you get the functionality you were going for.

这需要更多的工作,但您可以获得所需的功能。

UPDATE:remove SomeMethod from your interface and just do this

更新:从您的界面中删除 SomeMethod 并执行此操作

SomeMethod(myData.Data.Data);

回答by Hosam Aly

You can create a generic wrapper for SomeMethodand check for the type of the generic argument, then delegate to the appropriate method.

您可以为SomeMethod泛型参数创建一个泛型包装器并检查泛型参数的类型,然后委托给适当的方法。

public void SomeMethod<T>(T value)
{
    Type type = typeof(T);

    if (type == typeof(int))
    {
        SomeMethod((int) (object) value); // sadly we must box it...
    }
    else if (type == typeof(float))
    {
        SomeMethod((float) (object) value);
    }
    else if (type == typeof(string))
    {
        SomeMethod((string) (object) value);
    }
    else
    {
        throw new NotSupportedException(
            "SomeMethod is not supported for objects of type " + type);
    }
}