C#隐式转换运算符和/作为运算符

时间:2013-08-22 20:56:41

标签: c# .net type-conversion

我正在创建一个复合对象,我希望能够在其中一个包装对象可以使用的任何地方使用它。因此,给出以下对象:

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上的停用词(与大多数搜索引擎一样),这使得搜索包含这些关键字的问题变得非常困难。)

3 个答案:

答案 0 :(得分:7)

  

文档暗示不可能

文档是正确的。

  

有没有人知道是否有办法构建FooWrapper,因为转换是否有效?

我知道。那没有。 (显然,使FooWrapper成为Foo的派生类。)

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

  

实现像IConvertible这样的界面?

不。

  

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

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

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

答案 1 :(得分:2)

为了将FooWrapper视为FooFooWrapper必须从Foo继承。那就是:

class FooWrapper: Foo
{
}

但问题是,如果您希望包装器也包装Bar对象,则不能。因为C#不支持多重继承。

通常,如果您需要一个像多个对象一样的包装器,那么您可以实现接口。因此,例如,您将拥有:

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());

然后,您的测试将使用isas来检查接口而不是具体类。

var isFoo = wrapper is IFoo;
IFoo myFoo = wrapper as IFoo;

答案 2 :(得分:1)

is / as处理包装类的唯一选择是包装类是否确实是包装类。如果你想要包装的课程没有密封,你可以从中得到...

现在你想要实际实现的目标是不可能的 - .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();

如果您的代码使用接口与对象进行交互,那么您可以更轻松地完成任务,因为完全支持使用替代实现替换接口。手动包装或通过反射/发射自动创建都可以。

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 

我对为什么不考虑其他转换的看法:

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