Подтвердить что ты не робот

С#: Объявление и использование списка общих классов с разными типами, как?

Имея следующий общий класс, который будет содержать string, int, float, long как тип:

public class MyData<T>
{
    private T _data;

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

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

Я пытаюсь получить список MyData<T>, где каждый элемент будет иметь разные T.

Я хочу иметь доступ к элементу из списка и получить его значение, как в следующем коде:

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

где SomeMethod() объявляется следующим образом:

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

UPDATE:

SomeMethod() относится к другому классу классов. Я не контролирую и SomeMethod(object) не существует.


Однако я не могу найти способ сделать компилятор счастливым.

Любые предложения?

Спасибо.

4b9b3361

Ответ 1

Делегаты могут действительно помочь упростить это и по-прежнему держать вещи безопасными:

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

Ответ 2

Я думаю, что проблема, с которой вы сталкиваетесь, заключается в том, что вы пытаетесь создать общий тип, а затем создаете список этого родового типа. Вы можете выполнить то, что вы пытаетесь сделать, заключая контракты на типы данных, которые вы пытаетесь поддерживать, например, как элемент IData, а затем создайте свой общий набор MyData с ограничением IData. Недостатком этого было бы то, что вам придется создавать свои собственные типы данных для представления всех примитивных типов данных, которые вы используете (строка, int, float, long). Это может выглядеть примерно так:

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

а затем реализация будет выглядеть примерно так:

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

он немного больше работает, но вы получаете функциональность, в которой вы шли.

UPDATE: удалить SomeMethod из вашего интерфейса и просто сделать это

SomeMethod(myData.Data.Data);

Ответ 3

Просто используйте ArrayList и забудьте тип MyData<T>.

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

Проблема с MyData<T> заключается в том, что вы ожидаете, что компилятор проверяет тип, который известен только во время выполнения. Компиляторы проверяют типы, которые известны во время компиляции.

Ответ 4

Вы не можете сделать это так, как хотите.

Когда экземпляр родового класса инициализируется, он привязан к определенному типу. Поскольку вы хотите хранить объекты разных типов в своем списке, вам необходимо создать экземпляр, связанный с наименьшим общим знаменателем - в вашем случае это Object.

Однако это означает, что свойство Data теперь вернет объект типа Object. Компилятор не может вывести фактический тип данных во время компиляции, поэтому он может выбрать соответствующую перегрузку SomeMethod.

Вы должны либо предоставить перегрузку SomeMethod, которая принимает объект в качестве параметра, либо удалить требование о сохранении разных типов в вашей коллекции.

Или вы можете пойти со стандартной коллекцией IEnumerable (например, Array) и использовать метод расширения OfType<> для получения подмножества коллекции определенного типа.

Ответ 5

В этом случае вам нужно MyData<object>, так как это единственное, что есть у этих типов.

Ответ 6

Предлагаемые подстановочные знаки a назад здесь. Закрыто как "не будет исправлено": (

Ответ 7

Вы можете создать общую оболочку для 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);
    }
}

Ответ 8

Наследовать MyData<T> из не общего класса MyData и составить список этого.

Таким образом, вы не можете автоматически разрешить перегрузку. Вы должны сделать это вручную.

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 { ... }
   }
}

Ответ 9

Generics позволяет вам указать один тип для всего списка при создании списка, например, список для хранения int будет создан таким образом

var myData = new MyData<int>();

Если вы хотите хранить несколько типов в одном общем списке, вы можете указать общий базовый тип или интерфейс для этих типов. К сожалению, в вашем случае единственным распространенным базовым типом для типов, которые вы хотите сохранить, будет объект.

var myData = new MyData<object>();

Но вы можете просто использовать не общий список для хранения объектов.