2010-07-09 4 views
23

Если у меня есть общий класс:Коллекция родовых типов

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

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

new MyClass<string> 
new MyClass<int> 

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

+0

Как вы объявите свой класс? Как вы определили коллекцию резервных копий? – Oded

+0

это просто «гипотетический» или что-то, что вы пытаетесь использовать как часть решения? Я спрашиваю, потому что, учитывая немного больше контекста, мы могли бы предложить лучший ответ. :) – Rob

+0

Похоже, что те же вопросы были отправлены позже (я проголосовал за закрытие, но, возможно, не сделал это, так как он так стар): http: //stackoverflow.com/q/3777057/590790 –

ответ

23

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

Вот пример.

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

Возвращение типа является интересным вариантом. –

+1

Ваше решение, безусловно, элегантно. Сохранение внутреннего словаря типов делает более простые методы доступа. –

+0

Кроме того, теперь вы можете использовать ключевое слово 'dynamic': http://msdn.microsoft.com/en-us/library/dd264736.aspx – Tommy

1

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

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

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

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

var myItem = list[1]; 

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

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


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

7

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

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» к интерфейсу, который позволяет получить доступ к «Value» экземпляр MyClass как объект. Это примерно так же хорошо, как это получится, afaik, если вы хотите иметь коллекцию, содержащую смешанные типы.

+0

хороший вызов - это эквивалентный ответ на мой, используя базовый класс :) –

+0

Но как вы получаете доступ к свойству value? –

+0

Проблема в том, что он хочет иметь свойство Value, которое уже имеет правильный тип, который не может быть определен. – CodingWithSpike

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

Возможно, в вашем примере стоит отметить MyBase как абстрактный класс, чтобы сделать его более ясным, что это за намерение, .. :) – Rob

+0

Интересно ... Как перебирать список и проверять содержимое свойства Value? – Jeremy

+0

+1 от меня, в значительной степени та же идея, что и мой ответ :) – Rob

3

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

@ commongenius, вы подтвердили, что он не будет компилироваться? Он скомпилирован для меня просто отлично в VS2k8 – Rob

+0

К тому моменту, когда я опубликовал свой комментарий, исходный пост был изменен. Я изменил свой комментарий соответственно. –

3

У меня есть интерфейсы большинства моих родовых типов с «Нетипизированные» членов:

private interface IMyClass 
{ 
    object UntypedValue { get; } 
} 

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

    object UntypedValue { get { return Value; } } 
} 

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

+0

+1 для размещения как типизированных, так и нетипизированных значений. –

1

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

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

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

1

С .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 

*/ 
Смежные вопросы