简体   繁体   English

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

[英]C# Implicit Conversion Operator and is/as operator

I'm creating a composite object that I want to be able to be used anywhere one of its wrapped objects could be used. 我正在创建一个复合对象,我希望能够在其中一个包装对象可以使用的任何地方使用它。 So given the following objects: 因此给出以下对象:

public class Foo{}

public class FooWrapper{
    private Foo _foo = new Foo();

    public static implicit operator Foo(FooWrapper wrapper)
    {
       return wrapper._foo;
    }
}

I want to make the following tests pass: 我想通过以下测试:

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

I've taken a look at the MSDN documentation: 我看了一下MSDN文档:
is: http://msdn.microsoft.com/en-us/library/scekt9xw.aspx 是: http//msdn.microsoft.com/en-us/library/scekt9xw.aspx
as: http://msdn.microsoft.com/en-us/library/cscsdfbt%28v=vs.110%29.aspx as: http//msdn.microsoft.com/en-us/library/cscsdfbt%28v=vs.110%29.aspx

The is documentation implies it is not possible, IS文档意味着它是不可能的,

Note that the is operator only considers reference conversions, boxing conversions, and unboxing conversions. 请注意,is运算符仅考虑引用转换,装箱转换和取消装箱转换。 Other conversions, such as user-defined conversions, are not considered. 其他转换(例如用户定义的转化)不予考虑。

Does anyone know if there's a way to construct FooWrapper so is/as conversions will work? 有没有人知道是否有一种方法来构建FooWrapper所以是/因为转换将起作用? Implementing an interface like IConvertible perhaps? 或许实现像IConvertible这样的界面?

Bonus question: Anyone know why as/is doesn't consider user-defined conversions? 奖金问题:任何人都知道为什么不考虑用户定义的转换?

(And sorry if this question is a dup. 'as' and 'is' appear to be stop words here on stackoverflow (as with most search engines), making it quite difficult to search for questions that contain these keywords.) (对不起,如果这个问题是重复的话。'as'和'is'似乎是stackoverflow上的停止词(与大多数搜索引擎一样),这使得搜索包含这些关键词的问题变得非常困难。)

The is documentation implies it is not possible 文档意味着它是不可能的

The documentation is correct. 文档是正确的。

Does anyone know if there's a way to construct FooWrapper so is/as conversions will work? 有没有人知道是否有一种方法来构建FooWrapper所以是/因为转换将起作用?

I know. 我知道。 There is not. 那没有。 (Aside from, obviously, making FooWrapper a derived class of Foo .) (除此之外,显然,使FooWrapper成为Foo的派生类。)

The is and as operators are there to tell you what an object really is . isas运算符可以告诉你对象到底什么。 Don't try to make them lie. 不要试图让他们撒谎。

Implementing an interface like IConvertible perhaps? 或许实现像IConvertible这样的界面?

Nope. 不。

Anyone know why as/is doesn't consider user-defined conversions? 任何人都知道为什么/不考虑用户定义的转换?

First, because like I just said, the purpose of is is to tell you what the object really is . 首先,因为像我刚才说的,目的is要告诉你的对象真的是

Second, because suppose for the sake of argument the C# compiler team wished to add a new operator, say frob , that has the semantics of an as operator that uses user-defined conversions. 其次,因为假设为了参数,C#编译器团队希望添加一个新的运算符,比如frob ,它具有使用用户定义的转换的as运算符的语义。 x frob Foo gives you back a Foo when x is a FooWrapper . x frob FooxFooWrapper时, x frob Foo会给你一个Foo Describe please the IL you would like to see generated for that expression. 请描述您希望为该表达式生成的IL。 I think by engaging in this exercise you'll see why it is a hard problem. 我想通过参加这个练习,你会明白为什么这是一个难题。

In order for FooWrapper to be considered a Foo , FooWrapper would have to inherit from Foo . 为了将FooWrapper视为FooFooWrapper将不得不继承Foo That is: 那是:

class FooWrapper: Foo
{
}

The problem, though, is that if you wanted your wrapper to also wrap Bar objects, you can't. 但问题是,如果你想让你的包装器也包装Bar对象,你就不能。 Because C# does not support multiple inheritance. 因为C#不支持多重继承。

Typically if you need a wrapper that will act like multiple objects, you have it implement interfaces. 通常,如果您需要一个像多个对象一样的包装器,那么您可以实现接口。 So, for example, you would have: 所以,例如,您将拥有:

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;
    }
}

And of course MyWrapper would have to implement all the methods of IFoo and IBar , passing the calls on to the proper contained object. 当然, MyWrapper必须实现IFooIBar所有方法,将调用传递给适当的包含对象。 So if IFoo declared a Frob method, you would have (in the MyWrapper class): 因此,如果IFoo声明了一个Frob方法,你将(在MyWrapper类中):

    public void Frob()
    {
        _theFoo.Frob();
    }

That's implicit interface implementation. 这是隐式接口实现。 You might also have to look into explicit implementation of interface methods. 您可能还需要研究接口方法的显式实现。

To create a wrapper: 要创建包装器:

MyWrapper wrapper = new MyWrapper(new Foo(), new Bar());

Your tests, then, would use is and as to check for interfaces rather than concrete classes. 你的测试,然后,将使用isas检查的接口,而不是具体的类。

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

The only option for is / as to work with the wrapper class is if wrapping class is indeed wrapped class. is / as使用包装类的唯一选择is包装类是否确实是包装类。 If class you want to wrap is not sealed you can derive from it... 如果你想要包装的课程没有密封,你可以从中得到...

Now what you are trying to actually achieve is not possible in general case - .Net/C# statically detects what methods will be called and all non-virtual methods of your wrapped object must be called directly on object of that type (or derived one), but you'll not be able to override them in any way. 现在你想要实际实现的目标是不可能的 - .Net / C#静态地检测将调用哪些方法,并且必须直接在该类型的对象(或派生的对象)上调用包装对象的所有非虚方法,但你无法以任何方式覆盖它们。 Static methods are even harder. 静态方法更难。

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

If your code uses interfaces to interact with objects your task is much easier as substituting an interface with alternative implementation is completely supported. 如果您的代码使用接口与对象进行交互,那么您可以更轻松地完成任务,因为完全支持使用替代实现替换接口。 Either manual wrapper or automatically created via reflection/Emit would work. 无论是手动包装还是通过反射/ Emit自动创建都可以。

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 

My thoughts on why is/as do not consider other conversion: 我对为什么不考虑其他转换的想法:

  • expectation of the operation is to be very quick and light weight check - other conversions may need to create objects/unpredictable in how long it would take, 操作的期望是非常快速和轻量级检查 - 其他转换可能需要创建对象/不可预测的时间,
  • it significantly increases number of options that can be considered by compiler so slowing it down 它显着增加了编译器可以考虑的选项数量,从而减慢了速度
  • in many other cases where object are used conversions will not be considered, so may lead to inconsistent code behavior where is reports success but object fails to be used correctly (like instance added to list). 在其他许多情况下,使用对象的转换将不予考虑,因此可能导致不一致的代码的行为,其中is报道成功,但对象不能正确使用(类似的例子添加到列表)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM