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的派生类。)

isas运算符可以告诉你对象到底什么。 不要试图让他们撒谎。

或许实现像IConvertible这样的界面?

不。

任何人都知道为什么不考虑用户定义的转换?

首先,因为正如我刚才所说,目的是告诉你对象到底什么。

其次,因为假设为了参数,C#编译器团队希望添加一个新的运算符,比如frob ,它具有使用用户定义的转换的as运算符的语义。 x frob FooxFooWrapper时, x frob Foo会给你一个Foo 。 请描述您希望为该表达式生成的IL。 我想通过参加这个练习,你会明白为什么这是一个难题。

为了将FooWrapper视为FooFooWrapper将不得不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必须实现IFooIBar所有方法,将调用传递给适当的包含对象。 因此,如果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 

我想为什么/不考虑其他转换:

  • 操作的期望是非常快速和轻量级检查 – 其他转换可能需要创建对象/不可预测的时间,
  • 它显着增加了编译器可以考虑的选项数量,从而减慢了速度
  • 在许多其他使用对象的情况下,将不考虑转换,因此可能导致代码行为不一致,其中报告成功但对象无法正确使用(例如添加到列表中的实例)。