无法将C#通用对象添加到该通用类型的列表

时间:2019-02-07 11:24:41

标签: c# generics collections casting

很抱歉,问题名称不是那么明确,但是我找不到更好的答案...

这是我的问题:

我有一堂课:

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

由于我对通用类型应用了“ IComparable”约束,因此IMO应该可以工作。

有人可以指出我可能会搞砸的地方吗?

编辑:我已经尝试在Class WatchableVariable上实现IComparable,这导致了完全相同的错误:/

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

编辑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 :(得分:0)

您的收藏集是List<WatchableVariable<IComparable>>。您正在尝试向其中添加WatchableVariable<T>

由于通用差异规则,因此不允许这样做。假设您的WatchableVariable<T>类定义了此方法:

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

如果您有WatchableVariable<Foo>,则可以致电watchableVariable.Set(new Foo()),但不能致电{em> watchableVariable.Set((IComparable)foo)。那是有道理的。

根据您的情况,有人可以打电话给Add<Foo>(""),因此Watchables包含一个WatchableVariable<Foo>。然后,他们访问Watchables[0],这给了他们WatchableVariable<IComparable>,然后呼叫Set((IComparable)bar)。这将是不安全的,因为我们之前曾说过WatchableVariable<Foo>的{​​{1}}方法将只接受Set(T value),但是我们已经给了它任何旧的Foo! / p>

解决此问题的方法是使用协方差,仅在接口上可用。

首先,我们声明一个通用接口,并保证您只能使用IComparableT取出,并且永远不会将它们放入其中:

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; 可以得到类似的结果,但这不适用于您的情况)。


针对您的Edit 2

您的修改包含评论

in T

这是一个运行时断言。您必须像这样定义您的界面:

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

答案 1 :(得分:0)

我不知道您要达到什么目的,但是要完成该工作,您应该:使用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);
    }
}