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

时间:2014-07-31 07:53:07

标签: c# generics

更新

我会接受让代码工作Jon's answer to this other question是正确的。 this answer解释了为什么界面被视为引用类型。

我仍然想知道不同之处。为什么接口在泛型类型约束时不被视为引用类型?他们的一些设计原因是什么?我有一种感觉,唯一的原因可能是“他们就是这样”。

原始问题:

我正在将一个类转换为泛型类,但是我发现使用接口作为类型参数会有一些奇怪的行为。该类有一个字段和属性,它们是一种接口,例如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; }
    }
}

为什么通用版本不起作用? ==!=运营商不应该适用于所有类型吗?

错误只发生在属性设置器中,所以它让我想到,_magic字段实际上是一个盒装的IMagic还是其他一些引用类型?实际上它可以设置为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 framework v4.5.2。

3 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:0)

您无法在通用类型上使用!===)。

可以通过IMagic来实施IComparable

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

答案 2 :(得分:-1)

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

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

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

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

编辑:我刚注意到你甚至不需要!=在二传手中,所以整个问题都变成了静音。