使用接口作为泛型类型约束时发生编译错误

本文关键字:编译 错误 约束 泛型类型 接口 | 更新日期: 2023-09-27 18:15:56

更新:

为了让代码发挥作用,我接受Jon对另一个问题的回答是正确的。这个答案回答了为什么接口被当作引用类型对待。

不过我还是想知道有什么不同。当接口是泛型类型约束时,为什么不将其视为引用类型?这是他们设计的原因吗?我有一种感觉,唯一的原因可能是"他们就是这样"。

原始问题:

我正在将一个类转换为泛型类,但我发现使用接口作为类型参数有一些奇怪的行为。该类有一个字段和属性,它们是一种接口类型,例如IMagic。

public interface IMagic
{
    bool Magic { get; }
}
public class HasMagic
{
    private IMagic _magic;
    public IMagic Magic
    {
        get { return _magic; }
        set
        {
            if (value != _magic)
                _magic = value;
        }
    }
    public bool IsMagical
    {
        get { return _magic != null ? _magic.Magic : true; }
    }
}

我想将它们改为类型T,并使用类型参数where T : IMagic定义类。但是这样做会给我一个编译器错误Operator '!=' cannot be applied to operands of type 'T' and 'T'

public class HasMagic<T> where T : IMagic
{
    private T _magic;
    public T Magic
    {
        get { return _magic; }
        set
        {
            // Compiler error here!
            if (value != _magic)
                _magic = value;
        }
    }
    public bool IsMagical
    {
        // But no error here!?
        get { return _magic != null ? _magic.Magic : true; }
    }
}

那么,为什么通用版本不起作用呢?==!=运算符不应该适用于所有类型吗?

不过,这个错误只发生在属性setter中,所以它让我思考,_magic字段实际上是一个装箱的IMagic还是其他引用类型?事实上,它可以设置为null,这应该只适用于可为null的类型。正如下面的测试所示,IMagic结构(MagicStruct(运行良好,但为什么呢?如您所料,将HasMagic中的字段和属性更改为MagicStruct会导致编译错误。

public class MagicTests
{
    [Fact]
    public void SomeMagicTest()
    {
        var mag = new HasMagic();
        Assert.True(mag.IsMagical);
        mag.Magic = new MagicClass();
        Assert.False(mag.IsMagical);
        mag.Magic = new MagicStruct();
        Assert.True(mag.IsMagical);
        mag.Magic = null;
        Assert.True(mag.IsMagical);
    }
}
public class MagicClass : IMagic
{
    public bool Magic { get { return false; } }
}
public struct MagicStruct : IMagic
{
    public bool Magic { get { return true; } }
}

如果它有任何相关性的话,我使用的是.Net框架v4.5.2。

使用接口作为泛型类型约束时发生编译错误

T是一个类型参数,可以是类或结构,因为编译器不允许您执行类或结构中不存在的操作。你可以这样试试:

public T Magic
    {
        get { return _magic; }
        set
        {
            // Compiler error here!
              if (!EqualityComparer<T>.Default.Equals(_magic, value))
                _magic = value;
        }
    }

或者您可以在代码中使用Equals

 public T Magic
    {
        get { return _magic; }
        set
        {
            // Compiler error here!
            if (!value.Equals(_magic))
                _magic = value;
        }
    }

不能在泛型类型上使用!=(==(。

一个可能的解决方案是IMagic来实现可比较的

public interface IMagic : IComparable

然后使用CompareTo

 if (value.CompareTo(_magic) != 0)

您可以编写自己的CompareTo方法的实现

 public int CompareTo(object obj) {
    }

更新

如果不能编辑IMagic,请尝试向HasMagic类添加一个属性,如

public Func<T, T, bool> FuncEvaluate { get; set; } 

然后用这种方式检查

  public T Magic
    {
        get { return _magic; }
        set
        {
            if (FuncEvaluate != null)
            {
                if (!FuncEvaluate(value, _magic))
                {
                    _magic = value;
                }
            }
            else
            {
                throw new NotImplementedException();
            }
        }
    }

当您决定使用该类时,您应该设置函数来比较对象,可能会检查typeof(T).IsValueType

T可以是值类型或引用类型;你没有反对(and==(运算符(引用比较(仅为引用类型定义,而不为值类型定义。

有几种解决方案;您可以将T约束为参考类型:

public class HasMagic<T> where T : IMagic, class

您也可以使用Equals((。或对象。ReferenceEquals((。也许还有其他一些我没有想到的解决方案。

编辑:我刚刚注意到你根本不需要在setter中,所以整个问题变为静音。