使用接口作为泛型类型约束时发生编译错误
本文关键字:编译 错误 约束 泛型类型 接口 | 更新日期: 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中,所以整个问题变为静音。