C#隐式转换运算符,是/作为运算符
我正在创建一个复合对象,我希望能够在其中一个包装对象可以使用的任何地方使用它。 因此给出以下对象:
public class Foo{} public class FooWrapper{ private Foo _foo = new Foo(); public static implicit operator Foo(FooWrapper wrapper) { return wrapper._foo; } }
我想通过以下测试:
public class ConversionTests { private FooWrapper _uat = new FooWrapper(); [Test] public void Can_Use_Is_Operator() { Assert.IsTrue(_uat is Foo); } [Test] public void Can_Use_As_Operator() { Assert.IsTrue(null != _uat as Foo); } }
我看了一下MSDN文档:
是: http : //msdn.microsoft.com/en-us/library/scekt9xw.aspx
as: http : //msdn.microsoft.com/en-us/library/cscsdfbt%28v=vs.110%29.aspx
文档暗示它是不可能的,
请注意,is运算符仅考虑引用转换,装箱转换和拆箱转换。 其他转换(例如用户定义的转化)不予考虑。
有没有人知道是否有一种方法来构建FooWrapper所以是/因为转换将起作用? 或许实现像IConvertible这样的界面?
奖金问题:任何人都知道为什么不考虑用户定义的转换?
(对不起,如果这个问题是重复的话。’as’和’is’似乎是stackoverflow上的停用词(与大多数搜索引擎一样),这使得搜索包含这些关键字的问题变得非常困难。)
文档暗示它是不可能的
文档是正确的。
有没有人知道是否有一种方法来构建FooWrapper所以是/因为转换将起作用?
我知道。 那没有。 (除此之外,显然,使FooWrapper
成为Foo
的派生类。)
is
和as
运算符可以告诉你对象到底是什么。 不要试图让他们撒谎。
或许实现像IConvertible这样的界面?
不。
任何人都知道为什么不考虑用户定义的转换?
首先,因为正如我刚才所说,目的是告诉你对象到底是什么。
其次,因为假设为了参数,C#编译器团队希望添加一个新的运算符,比如frob
,它具有使用用户定义的转换的as
运算符的语义。 x frob Foo
当x
是FooWrapper
时, x frob Foo
会给你一个Foo
。 请描述您希望为该表达式生成的IL。 我想通过参加这个练习,你会明白为什么这是一个难题。
为了将FooWrapper
视为Foo
, FooWrapper
将不得不inheritanceFoo
。 那是:
class FooWrapper: Foo { }
但问题是,如果您希望包装器也包装Bar
对象,则不能。 因为C#不支持多重inheritance。
通常,如果您需要一个像多个对象一样的包装器,那么您可以使用它来实现接口。 所以,例如,您将拥有:
public interface IFoo { } public class Foo: IFoo // implements the IFoo interface { } public interface IBar { } public class Bar: IBar { } public class MyWrapper: IFoo, IBar // Implements the IFoo and IBar interfaces { private IFoo _theFoo; private IBar _theBar; public MyWrapper(IFoo foo, IBar bar) { _theFoo = foo; _theBar = bar; } }
当然, MyWrapper
必须实现IFoo
和IBar
所有方法,将调用传递给适当的包含对象。 因此,如果IFoo
声明了一个Frob
方法,那么(在MyWrapper
类中):
public void Frob() { _theFoo.Frob(); }
这是隐式接口实现。 您可能还需要研究接口方法的显式实现。
要创建包装器:
MyWrapper wrapper = new MyWrapper(new Foo(), new Bar());
那么,你的测试将用于检查接口而不是具体的类。
var isFoo = wrapper is IFoo; IFoo myFoo = wrapper as IFoo;
is
/ as
使用包装类的唯一选择is
包装类是否确实是包装类。 如果你想要包装的课程没有密封,你可以从中得到…
现在你想要实际实现的目标是不可能的 – .Net / C#静态地检测将调用哪些方法,并且必须直接在该类型的对象(或派生的对象)上调用包装对象的所有非虚方法,但你无法以任何方式覆盖它们。 静态方法更难。
var item = new InnerType(); // you can't create any class that will replace method in this call item.NonVirtual(); // No way to replace static method with wrapper var result = InnerType.StaticMethod(); // At least virtual methods can be overriden if Wrapper derives from InnerType item.VirtualMethod();
如果您的代码使用接口与对象进行交互,那么您可以更轻松地完成任务,因为完全支持使用替代实现替换接口。 手动包装或通过reflection/发射自动创建都可以。
var itemByInterface = (IInnerType)Factory.CreateInnerType(); itemByInterface.InterfaceMethod(); // is/as checks should use interface var isRightType = itemByInterface is IInnerType; // do not use static/non-interface calls
我想为什么/不考虑其他转换:
- 操作的期望是非常快速和轻量级检查 – 其他转换可能需要创建对象/不可预测的时间,
- 它显着增加了编译器可以考虑的选项数量,从而减慢了速度
- 在许多其他使用对象的情况下,将不考虑转换,因此可能导致代码行为不一致,其中报告成功但对象无法正确使用(例如添加到列表中的实例)。