2013-07-08 3 views
0

У меня проблема с подстановочными знаками в generics в C#. Первый подход, чтобы запустить мой маленький пример, состоял в том, чтобы использовать объект как общий тип, потому что он является базовым классом всего.C# общий подтип супертипа

public class AttributeManager 
    { 

     private Dictionary<int, AttributeItem<object>> attributes = new Dictionary<int, AttributeItem<object>>(); 

     public void add(AttributeItem<object> attribute) 
     { 
      if (hasAttribute(attribute)) { 
       return; 
      } 
      attributes.Add(attribute.getKey(), attribute); 
     } 
    } 

public abstract class AttributeItem<T> 
{ 
    private int key; 
    private T attributeValue; 
    private AttributeManager attributeManager; 

    public AttributeItem(AttributeManager attributeManager, int key) 
    { 
     this.key = key; 
     this.attributeManager = attributeManager; 
     attributeManager.add(this); // this line does not work 
    } 

    public void setValue(T newValue) 
    { 
     attributeValue = newValue; 
    } 

    public T getValue() 
    { 
     return attributeValue; 
    } 
} 

Однако линия:

attributeManager.add (это);

не работает. В нем говорится, что для этого вызова не было перегруженного метода. Я думал, что «это» будет передано в AttributeItem, потому что объект должен быть суперклассом T. Итак, мой первый вопрос: почему этот актер не работает?

Мой второй подход был изменить AttributeManager использовать вид маски:

public class AttributeManager 
    { 

     private Dictionary<int, AttributeItem<????>> attributes = new Dictionary<int, AttributeItem<????>>(); 

     /** 
     * This method will add a new AttributeItem if hasAttribute(AttributeItem) returns false. 
     */ 
     public void add<T>(AttributeItem<T> attribute) 
     { 
      if (hasAttribute(attribute)) { 
       return; 
      } 
      attributes.Add(attribute.getKey(), attribute); // this line fails 
     } 

    } 

Но, как вы можете видеть, я понятия не имею, какой тип я должен передать в объявлении:

Dictionary<int, AttributeItem<????>> attributes 

Так что мой второй вопрос: что мне нужно использовать вместо ?????

С уважением Роберт

+0

А, да, у меня была такая же проблема при разработке решения, и я лично переделал его. Я думаю, вы пытаетесь избежать , находясь в объявлении класса AttributeManager? –

+0

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

+0

Вместо этого вы можете использовать интерфейс и изменить свой AttributeItem так, чтобы он имел ограничение T: YourInterface? Таким образом, код будет выглядеть как частный словарь > attributes = new Dictionary >(); –

ответ

2

Самое простое решение, чтобы избавиться от дженериков на уровне вашего личного словаря поля:

private Dictionary<int, object> attributes = new Dictionary<int, object>(); 

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

Сложная часть получает что-то полезное из словаря позже. Вы можете использовать отражение, но я предлагаю вам использовать технику интерфейса, предложенную Онамом и Робертом Ханом. Расскажите нам больше о своем usecase, если это не решит вашу проблему.

0

Вы должны иметь что-то вроде этого:

public class AttributeManager<T> 
{ 

    private Dictionary<int, AttributeItem<T>> attributes = new Dictionary<int, AttributeItem<T>>(); 

    public void add(AttributeItem<T> attribute) 
    { 
     if (hasAttribute(attribute)) { 
      return; 
     } 
     attributes.Add(attribute.getKey(), attribute); 
    } 

} 
+0

Но мой AttributeManager также является общим. Но я хочу, чтобы он принял любые Атрибуты. Я забыл об этом. –

0

Тем не менее, линия:

attributeManager.add (это);

не работает. В нем говорится, что для этого вызова не было перегруженного метода. Я думал, что> «это» получит отлиты в AttributeItem, потому что объект должен быть суперкласс Т

Вы должны прочитать о covariance and contravariance. В принципе, только потому, что T конвертируется в базовый тип, не означает, что общий интерфейс есть. Все зависит от , для чего используется интерфейс.

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

В зависимости от того, что вам нужно для do с AttributeItem, вы можете определить контравариантный интерфейс с необходимыми значениями общего возврата для AttributeManager.