更新
我会接受让代码工作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。
答案 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()。 也许还有其他一些我没有想到的解决方案。
编辑:我刚注意到你甚至不需要!=在二传手中,所以整个问题都变成了静音。