Коллекция универсальных типов - PullRequest
29 голосов
/ 09 июля 2010

Если у меня есть общий класс:

public class MyClass<T> 
{
  public T Value;
}

Я хочу создать несколько экземпляров, таких как ...

new MyClass<string>
new MyClass<int>

... и добавить их в коллекцию.Как определить коллекцию, чтобы она могла содержать список универсальных типов?Затем я хочу перебрать коллекцию и использовать свойство Value.Возможный?

Ответы [ 9 ]

29 голосов
/ 09 июля 2010

Унаследуйте свой универсальный класс от неуниверсальной базы или реализуйте неуниверсальный интерфейс. Затем вы можете иметь коллекцию этого типа и преобразовывать ее в любой код, который вы используете для доступа к содержимому коллекции.

Вот пример.

public abstract class MyClass
{
    public abstract Type Type { get; }
}

public class MyClass<T> : MyClass
{
    public override Type Type
    {
        get { return typeof(T); }
    }

    public T Value { get; set; }
}

// VERY basic illustration of how you might construct a collection
// of MyClass<T> objects.
public class MyClassCollection
{
    private Dictionary<Type, MyClass> _dictionary;

    public MyClassCollection()
    {
        _dictionary = new Dictionary<Type, MyClass>();
    }

    public void Put<T>(MyClass<T> item)
    {
        _dictionary[typeof(T)] = item;
    }

    public MyClass<T> Get<T>()
    {
        return _dictionary[typeof(T)] as MyClass<T>;
    }
}
8 голосов
/ 09 июля 2010

Единственное, что я могу придумать, это, как мне показалось, (заключить в консольное приложение для тестирования):

class Program
{
    static void Main(string[] args)
    {
        var x = new MyClass<string>() { Value = "34" };
        var y = new MyClass<int>() { Value = 3 };

        var list = new List<IMyClass>();
        list.Add(x);
        list.Add(y);

        foreach (var item in list)
        {
            Console.WriteLine(item.GetValue);
        }
    }

    private interface IMyClass
    {
        object GetValue { get; }
    }

    private class MyClass<T> : IMyClass
    {
        public T Value;

        public object GetValue
        {
            get
            {
               return Value;
            }
        }
    }
}

т.е. MyClass должен реализовать пустой интерфейс, а затем создатьваши коллекции как экземпляры, которые содержат экземпляры классов, которые реализуют этот интерфейс.

Обновление: Я добавил метод "GetValue" в интерфейс, который позволяет вам получить доступ к "Значение"экземпляр MyClass как объект.На самом деле, это почти так же хорошо, как если бы вы хотели иметь коллекцию, которая содержит смешанные типы.

3 голосов
/ 09 июля 2010

У меня есть интерфейсы на большинстве моих универсальных типов с элементами "Untyped":

private interface IMyClass
{
    object UntypedValue { get; }
}

private class MyClass<T> : IMyClass
{
    T Value { get; set; }

    object UntypedValue { get { return Value; } }
}

Вы также можете сделать это с помощью явной реализации интерфейса, но, на мой взгляд, это гораздо прощеиспользуя отдельное имя.(Также есть несколько советов CA о явной реализации интерфейса)

3 голосов
/ 09 июля 2010

Вы хотите иметь коллекцию MyClass, для которой значение параметра типа T отличается в каждом экземпляре.Это невозможно в .NET;ему не хватает эквивалента подстановочного знака Java (?).Вместо этого вам нужно создать неуниверсальный базовый класс или интерфейс, который может реализовать MyClass.Например:

public interface IMyClass {
  object Value { get; set; }
}
public class MyClass<T> : IMyClass {
  public T Value { get; set; }
  object IMyClass.Value {
    get { return Value; }
    set { Value = (T)value; }
  }
}
List<IMyClass> m = new List<IMyClass> { new MyClass<string>(), new MyClass<int> };
3 голосов
/ 09 июля 2010

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

void Main()
{
 var a = new MyClass<string>();
 var b = new MyClass<int>();
 var c = new List<MyBase>();
 c.Add(a);
 c.Add(b);

}

public class MyBase { }
// Define other methods and classes here
public class MyClass<T> : MyBase {
public T Value { get; set;}
}
2 голосов
/ 04 ноября 2012

Начиная с .Net 3 существует класс CompositeCollection , который позволяет содержать в себе несколько уникальных элементов или даже коллекций. Он используется разработчиками WPF для хранения и отображения различных элементов в Xaml. Но это не значит, что его нельзя использовать в ситуациях, не связанных с WPF.

Вот пример, где я храню разные вещи от строк до десятичных знаков, извлекаю и перечисляю все элементы, затем элементы определенного типа:

CompositeCollection cc = new CompositeCollection();

cc.Add(1);
cc.Add("Item One");
cc.Add(1.0M);
cc.Add(1.0);
cc.Add("Done");

Console.WriteLine ("Every Item");

foreach (var element in cc)
    Console.WriteLine ("\t" + element.ToString());

Console.WriteLine (Environment.NewLine + "Just Strings");

foreach (var str  in cc.OfType<string>())
    Console.WriteLine ("\t" + str);

/* Outputs:

Every Item
  1
  Item One
  1.0
  1
  Done

Just Strings
  Item One
  Done

*/
2 голосов
/ 09 июля 2010

Я думаю, что проблема в том, что универсальные классы на самом деле совсем не одного типа.Это просто шаблоны, которые создают совершенно новые типы во время компиляции (если я правильно понимаю).Следовательно, MyClass<int> и MyClass<string> являются полностью различными типами в зависимости от времени выполнения.Это также могут быть MyIntClass и MyStringClass, которые вы, очевидно, не можете иметь в одном и том же списке, не поместив их в бокс.Они не (обязательно) наследуют один и тот же базовый класс, реализуют те же интерфейсы или что-то еще.Они такие же разные, как и любые другие два типа, и вы должны обращаться с ними как с такими (даже если вы думаете, что знаете лучше).

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

1 голос
/ 09 июля 2010

IList

Нет способа определить универсальную коллекцию, которая может принять любой вариант вашего универсального класса ... т.е. IList<MyClass>. Универсальные классы - это всего лишь короткий путь для разработчика, чтобы сэкономить на написании связки повторяющегося кода, но во время компиляции каждый вариант универсального класса переводится в конкретный. То есть, если у вас есть MyClass<string>, MyClass<int>, MyClass<bool>, то компилятор сгенерирует 3 отдельных и отдельных класса. Единственный способ сделать то, что вы хотите, это иметь интерфейс для вашего универсального.

public interface IMyGeneric {
    Type MyType { get; set;}    
}

class MyGeneric<T> : IMyGeneric {

    public MyGeneric() {
        MyType = typeof(T);
    }

    public Type MyType {
        get; set;
    }
}

и тогда вы можете сказать

IList<IMyGeneric> list = new List<IMyGeneric>();
list.add(new MyGeneric<string>());
list.add(new MyGeneric<int>());
1 голос
/ 09 июля 2010

Я полагаю, что ваша коллекция должна быть одного типа MyClass (как в T должна быть одинаковой), потому что компилятор не будет знать, какие типы вы добавили к каким элементам в коллекции.

Другими словами, если вам нужно добавить 2 элемента в список:

list.Add(new MyClass<string>());
list.Add(new MyClass<int>());

, попробуйте сослаться на один из них:

var myItem = list[1];

Компилятор не знаеткакое обобщение было назначено списку MyClass в элементе 1 , потому что элементы добавляются во время выполнения, но обобщенные значения определяются во время компиляции.

Я почти уверен, что вы хотитесделать нельзя.


Если вы заранее знаете количество элементов, возможно, вы могли бы использовать Tuple ?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...