Коллекция общих типов

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

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

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

new MyClass<string>
new MyClass<int>

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

Ответ 1

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

Вот пример.

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

Ответ 2

Единственный способ, о котором я могу думать, от верхней части моей головы - сделать следующее (завершено в приложении Console для тестирования):

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 реализует пустой интерфейс, а затем создаст ваши коллекции как те, которые содержат экземпляры классов, реализующих этот интерфейс.

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

Ответ 3

Вы хотите определить базовый класс для 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;}
}

Ответ 4

Вы хотите иметь коллекцию 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> };

Ответ 5

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

private interface IMyClass
{
    object UntypedValue { get; }
}

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

    object UntypedValue { get { return Value; } }
}

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

Ответ 6

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

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

Ответ 7

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

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

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

затем попытайтесь ссылаться на один:

var myItem = list[1];

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

Я уверен, что то, что вы хотите сделать, не может быть сделано.


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

Ответ 8

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

Ответ 9

Так как .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

*/