我正在创建一个复合对象,我希望能够在其中一个包装对象可以使用的任何地方使用它。因此,给出以下对象:
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上的停用词(与大多数搜索引擎一样),这使得搜索包含这些关键字的问题变得非常困难。)
答案 0 :(得分:7)
文档暗示不可能
文档是正确的。
有没有人知道是否有办法构建FooWrapper,因为转换是否有效?
我知道。那没有。 (显然,使FooWrapper
成为Foo
的派生类。)
is
和as
运算符可以告诉您对象到底是什么。不要试图让他们撒谎。
实现像IConvertible这样的界面?
不。
任何人都知道为什么/不考虑用户定义的转换?
首先,因为正如我刚才所说,is
的目的是告诉你对象到底是什么。
其次,因为假设为了参数,C#编译器团队希望添加一个新的运算符,比如frob
,它具有使用用户定义的转换的as
运算符的语义。当x frob Foo
为Foo
时,x
会为您提供FooWrapper
。请描述您希望为该表达式生成的IL。我想通过参加这个练习,你会明白为什么这是一个难题。
答案 1 :(得分:2)
为了将FooWrapper
视为Foo
,FooWrapper
必须从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
必须实现IFoo
和IBar
的所有方法,将调用传递给正确的包含对象。因此,如果IFoo
声明了Frob
方法,那么(在MyWrapper
类中):
public void Frob()
{
_theFoo.Frob();
}
这是隐式接口实现。您可能还需要研究接口方法的显式实现。
创建包装器:
MyWrapper wrapper = new MyWrapper(new Foo(), new Bar());
然后,您的测试将使用is
和as
来检查接口而不是具体类。
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
报告成功但对象无法正确使用(例如添加到列表中的实例)。