【问题标题】:C# generic object cannot be added to list of that generic typeC# 泛型对象不能添加到该泛型类型的列表中
【发布时间】:2019-07-01 12:08:44
【问题描述】:

对不起,如果问题名称不是那么明确,但我找不到更好的...

这是我的问题:

我有一堂课:

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

  ...
}

还有一个奇怪的错误:

哪个,IMO,应该可以工作,因为我在我的泛型类型上应用了“IComparable”约束。

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

编辑:我已经尝试在 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");

这会让事情更清楚吗?

【问题讨论】:

  • 这段代码对我来说没有意义。你想达到什么目标?
  • class WatchableVariable&lt;T&gt; 中没有where T : IComparable 这样的约束。
  • @AlessandroD'Andria - 你测试过这是否正确吗?不,这就是答案。
  • @Enigmativity 抱歉,但我没有说这可以解决问题。我只是注意到了这一点。

标签: c# generics collections casting


【解决方案1】:

您的收藏是List&lt;WatchableVariable&lt;IComparable&gt;&gt;。您正在尝试向其添加WatchableVariable&lt;T&gt;

这是不允许的,因为通用方差的规则。假设您的 WatchableVariable&lt;T&gt; 类定义了这个方法:

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

如果你有WatchableVariable&lt;Foo&gt;,你可以打电话给watchableVariable.Set(new Foo()),但不是watchableVariable.Set((IComparable)foo)。这是有道理的。

根据你的情况,有人可以打电话给Add&lt;Foo&gt;(""),所以Watchables包含WatchableVariable&lt;Foo&gt;。然后他们访问Watchables[0],这给了他们一个WatchableVariable&lt;IComparable&gt;,并调用Set((IComparable)bar)。这将是不安全的,因为我们之前说过WatchableVariable&lt;Foo&gt;Set(T value) 方法将只接受Foo,但我们只是给了它任何旧的IComparable

解决这个问题的方法是使用协方差,它只在接口上可用。

首先,我们声明一个通用接口,并且我们保证您只能使用TT 从中取出,并且永远不要将它们放入其中,使用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&lt;T&gt; 类将不得不进行运行时强制转换,以确保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;
    }
}

【讨论】:

  • Daaaamn,我尝试使用协方差,但认为这会浪费时间:/ 我会立即尝试 ;-)
  • @AxelSamyn 请记住接受可以解决您问题的答案
  • 是的,对不起,我花了一些时间来处理你的答案 x) 我在我的问题中添加了一个 EDIT 2,我想我不能用你的解决方案将数据设置到我的列表中,因为我不能写这个: 公共接口 IWatchableVariable { T 变量 { get;放; } } ...我希望被允许设置该变量
  • 没错。正如我的问题所解释的那样,那将是不安全的。你可以构造一个WatchableVariable&lt;Foo&gt;,然后调用watchableVariable.Variable = "test"。这显然是不正确的,但这是允许的。
  • 确实:/ 是否有安全的方法来实现这一目标?
【解决方案2】:

我不知道你想达到什么目的,但为了实现这一目标,你应该:用 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);
    }
}

【讨论】:

  • 我怀疑 OP 想要一个普通的 CommonWatchableVariableContainer 而不是每个 T 一个。
  • @Enigmativity 也许,我真的不确定 OP 试图实现什么。
猜你喜欢
  • 2010-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多