简体   繁体   English

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

[英]Compile error using an interface as a generic type constraint

Update: 更新:

I'll accept that to get the code to work Jon's answer to this other question is correct. 我接受让代码起作用的乔恩对另一个问题的回答是正确的。 And this answer answers why an interface is treated like a reference type. 这个答案回答了为什么将接口视为引用类型。

I'd still like to know what the difference is though. 我仍然想知道有什么区别。 Why is an interface not treated like a reference type when it is a generic type constraint? 当接口是通用类型约束时,为什么不将其视为引用类型? Is their some design reason for this? 他们的某些设计原因吗? I have a feeling the only reason may be "they just are". 我觉得唯一的原因可能是“他们就是”。

Original question: 原始问题:

I am converting a class into a generic class but I found some strange behaviour with using an interface as a type parameter. 我正在将一个类转换为一个泛型类,但是使用接口作为类型参数时发现了一些奇怪的行为。 The class has a field and property which are a type of interface, for example IMagic. 该类具有作为接口类型的字段和属性,例如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; }
    }
}

I want to change them to instead be type T and have the class defined with the type parameter where T : IMagic . 我想将它们更改为T类型where T : IMagic类型参数定义类。 Doing this though gave me a compiler error Operator '!=' cannot be applied to operands of type 'T' and 'T' . 这样做虽然给了我一个编译器错误,但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; }
    }
}

So why doesn't the generic version work? 那么,通用版本为什么不起作用? Shouldn't == and != operators work for all types? ==!=运算符不应该对所有类型都适用吗?

The error only occurs in the property setter though, so it got me thinking, is the _magic field actually really a boxed IMagic or some other reference type? 该错误仅发生在属性设置器中,因此让我思考, _magic字段实际上是盒装IMagic还是其他引用类型? Indeed it can be set to null which should only work with nullable types. 实际上,可以将其设置为null,它仅适用于可为null的类型。 As the test below shows an IMagic struct (MagicStruct) works fine, but why? 如下面的测试所示,IMagic结构(MagicStruct)可以正常工作,但是为什么呢? Changing the field and property in HasMagic to a MagicStruct gives compile errors, as you'd expect. 如您所料,将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; } }
}

If it's of any relevance I'm using .Net framework v4.5.2. 如果有任何相关性,我正在使用.Net Framework v4.5.2。

T is a type argument and can be a class or a struct, because of that the compiler won't let you perform actions that doesn't exist in a class or a struct. T是类型参数,可以是类或结构,因为编译器不允许您执行类或结构中不存在的操作。 You can try it like this: 您可以这样尝试:

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

Or you can just use Equals inside your code 或者您也可以在代码中使用Equals

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

You cannot use != ( == ) on generic types. 您不能在泛型类型上使用!=== )。

a possible solution would be made IMagic to implement IComparable 一个可能的解决方案将成为IMagic以实现IComparable

public interface IMagic : IComparable

and then use CompareTo 然后使用CompareTo

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

you can write your own implementation of CompareTo method 您可以编写自己的CompareTo方法的实现

 public int CompareTo(object obj) {

    }

update 更新

if you can't edit IMagic try adding a property to HasMagic class like this 如果您无法编辑IMagic尝试向HasMagic类添加属性,如下所示

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

and then check in this way 然后以这种方式检查

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

and when you decide to use the class you should set the function to compare objects maybe checking typeof(T).IsValueType 当您决定使用该类时,应设置该函数以比较对象,例如可以检查typeof(T).IsValueType

T can be a value type or a reference type; T可以是值类型或引用类型; you didn't contrain it. 你没有禁忌。 The != (and ==) operator (reference comparison) is only defined for reference types, not for value types. !=(和==)运算符(引用比较)仅为引用类型定义,而不为值类型定义。

There are several solutions; 有几种解决方案。 You can constrain T to be a reference type: 您可以将T约束为引用类型:

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

You can also use Equals(). 您也可以使用Equals()。 or object.ReferenceEquals(). 或object.ReferenceEquals()。 And maybe some other solutions I didn't think of. 也许还有其他我没想到的解决方案。

Edit: I just noticed you don't even need != in the setter, so this whole problem becomes mute. 编辑:我只是注意到您甚至不需要在设置器中使用!=,因此整个问题变得无声。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM