Универсальный объект C # не может быть добавлен в список этого универсального типа - PullRequest
0 голосов
/ 07 февраля 2019

Извините, если название вопроса не было таким явным, но я не могу найти лучшего ...

Вот моя проблема:

У меня есть класс:

public class WatchableVariable<T>{
  ...
}

Класс контейнера:

public class CommonWatchableVariableContainer
{
  private List<WatchableVariable<IComparable>> Watchables;

  ...

  public void Add<T>(string name) where T : IComparable
  {
    WatchableVariable<T> test = new WatchableVariable<T>(name);
    Watchables.Add(test);
  }

  ...
}

И есть странная ошибка:

enter image description here

Что, IMO, должноработать, так как я применяю ограничение "IComparable" к своему родовому типу.

может кто-нибудь указать мне, где я мог испортить?

РЕДАКТИРОВАТЬ : я ужепопытался реализовать IComparable для класса WatchableVariable, что привело к точно такой же ошибке: /

public class WatchableVariable<T> : ObservableObject where T : IComparable
  { ... }

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

Вот интерфейс, который я создал:

public interface IWatchableVariable<out T>
{
  T Variable { get; set; }
}

Вот то, чего я пытаюсь достичь:

double dVar1 = process();
double dVar2 = process();
double dVar3 = process();

bool bVar1 = process();

CommonWatchableVariableContainer container = new CommonWatchableVariableContainer ();
container.Add<bool>("myTrackedVariable_1");
container.Add<double>("myTrackedVariable_2");

container["myTrackedVariable_1"].Variable = dVar1; //Variable would be a property of type T (which would be double in this case)
container["myTrackedVariable_2"].Variable = bVar1;

foreach(IWatchableVariable WV in container){
  process(WV);
}

container.Remove("myTrackedVariable_1");

Это проясняет ситуацию?

Ответы [ 2 ]

0 голосов
/ 07 февраля 2019

Я не знаю, чего вы пытаетесь достичь, но для того, чтобы это сработало, вы должны: объявить ваш тип с ограничением IComparable.

public class WatchableVariable<T> where T : IComparable
{
    public WatchableVariable(string name) { }
}

Затем сделать ваш второй класс обобщенным с помощьюто же ограничение.

public class CommonWatchableVariableContainer<T> where T : IComparable
{
    private List<WatchableVariable<T>> Watchables;

    public void Add(string name)
    {
        var test = new WatchableVariable<T>(name);
        Watchables.Add(test);
    }
}
0 голосов
/ 07 февраля 2019

Ваша коллекция List<WatchableVariable<IComparable>>.Вы пытаетесь добавить WatchableVariable<T> к нему.

Это не разрешено из-за правил общего отклонения.Давайте представим, что ваш WatchableVariable<T> класс определил этот метод:

public class WatchableVariable<T>
{
    public void Set(T value);
}

Если у вас есть WatchableVariable<Foo>, вы можете позвонить watchableVariable.Set(new Foo()), но , а не watchableVariable.Set((IComparable)foo).Это имеет смысл.

Примите это к вашей ситуации, и кто-то может позвонить Add<Foo>(""), поэтому Watchables содержит WatchableVariable<Foo>.Затем они получают доступ к Watchables[0], что дает им WatchableVariable<IComparable>, и звонят Set((IComparable)bar).Это было бы небезопасно, так как мы ранее говорили, что WatchableVariable<Foo> метод Set(T value) будет принимать только Foo, но мы только что дали ему любой старый IComparable!

Способ обойтиэто использует ковариацию, которая доступна только на интерфейсах.

Во-первых, мы объявляем универсальный интерфейс, и мы обещаем, что вы можете когда-либо только извлечь из него T и никогда не вставлять их,используя out T:

public interface IWatchableVariable<out T>
{
    // The compiler stops you from defining any methods which accept a T
}

public class WatchableVariable<T> : IWatchableVariable<T>
{
    // ...
}

Тогда вы можете написать:

private List<IWatchableVariable<IComparable>> Watchables;

И все будет работать.

(Аналогичная вещь доступна с использованием контравариантности и in T, но это не применимо к вашей ситуации).


В ответ на ваши правки 2

Ваша правка содержит комментарий

// Variable would be a property of type T (which would be double in this case)

Это утверждение во время выполнения.Вы должны будете определить свой интерфейс следующим образом:

public interface IWatchableVariable<out T>
{
    object Variable { get; set; }
}

Поскольку Variable не относится к типу T, это разрешено.Тогда вашему классу WatchableVariable<T> придется выполнить приведение во время выполнения, чтобы убедиться, что для Variable задан совместимый тип.Вы можете использовать явную реализацию интерфейса, чтобы сделать это немного аккуратнее.

public class WatchableVariable<T>
{
    public T Variable { get; set; }

    object IWatchableVariable<T>.Variable
    {
        get => Variable;
        set => Variable = (T)value;
    }
}

Вы можете сделать это немного аккуратнее, выполнив что-то вроде:

public interface IWatchableVariable<out T>
{
    T Variable { get; }
    void SetVariable(object value);
}

public class WatchableVariable<T> : IWatchableVariable<T>
{
    public T Variable { get; set; }
    public void SetVariable(object value)
    {
        Variable = (T)value;
    }
}
...