关于“as”关键字的隐式/显式转换

我正在尝试对一个不幸具有高度单位相互依赖性的项目进行一些unit testing。 目前,我们的很多类都在寻找一个自定义的UserIdentity对象来确定身份validation,但是这个对象有很多内部的跳跃,我试图测试单个单元的function时会很快避免。

为了解决其中一些问题,我正在尝试创建一个UserIdentity的“模拟”版本,可以通过更严格控制的变量环境插入。

简而言之,我们有一个UserIdentity类,它有几个公共只读属性和一个静态CurrentIdentity( IIdentity )占位符。 我可以通过“模拟” IIdentity实现解决所有问题,但是当我达到将CurrentIdentity作为UserIdentity进行投射时,我遇到了障碍

这是一个非常简单的方法:

internal static UserIdentity GetCurrentIdentity() { UserIdentity currentIdentity = ApplicationContext.User.Identity as UserIdentity; return currentIdentity; } 

我已经设置了我的模拟对象来创建UserIdentity类型的成员,然后执行以下操作:

  public static implicit operator UserIdentity(MockUserIdentity src) { return src.UserIdentity; } 

或这个

  public static explicit operator UserIdentity(MockUserIdentity src) { return src.UserIdentity; } 

问题是,据我所知,’as’似乎没有在我的模拟对象上调用隐式或显式转换操作。 我的问题是(是吗?),我在这里遗漏了一些简单的东西,或者这不起作用,因为(我猜)’as’操作直接看到类inheritance(我的对象不做…) ?

此外,可能有点偏离主题,但为什么在类中不能同时使用相同结果类型的显式和隐式运算符? 除非我遗漏了一些愚蠢的东西,否则如果我尝试同时拥有两个转换运算符,编译器就会出现问题。 我必须选择一个或另一个。

UPDATE

好的,现在我完全糊涂了。 也许我变得邋,,但我已经尝试过做直接演员,我似乎无法让它发挥作用。 我在MSDN上阅读了运算符,示例显示运算符进入结果类而不是源类,但我不确定这是否重要(我在下面的代码中尝试了两个位置)。 无论哪种方式,我试图建立一个简单的试验台,看看我可能做错了什么,但我也无法让它工作……这就是我所拥有的

 class Program { // Shared Interface public interface IIdentity { } // "real" class (not conducive to inheritence) public class CoreIdentity : IIdentity { internal CoreIdentity() { } // Just in case (if this has to be here, that seems unfortunate) public static explicit operator CoreIdentity(ExtendedIdentity src) { return src.Identity; } } // "mock" class (Wraps core object) public class ExtendedIdentity : IIdentity { public CoreIdentity Identity { get; set; } public ExtendedIdentity() { Identity = new CoreIdentity(); } // This is where the operator seems like it should belong... public static explicit operator CoreIdentity(ExtendedIdentity src) { return src.Identity; } } // Dummy class to obtain "current core identity" public class Foo { public IIdentity Identity { get; set; } public CoreIdentity GetCoreIdentity() { return (CoreIdentity)Identity; } } static void Main(string[] args) { ExtendedIdentity identity = new ExtendedIdentity(); Foo foo = new Foo(); foo.Identity = identity; CoreIdentity core = foo.GetCoreIdentity(); } } 

但是当我调用foo.GetCoreIdentity()时会抛出以下exception:

无法将“ExtendedIdentity”类型的对象强制转换为“CoreIdentity”。

并且我无法捕获具有断点的任何显式运算符,因此看起来它甚至没有“尝试”我提供的转换路径就做出了这个决定。

当然,我错过了一些明显的东西。 我将我的身份(在Foo中)定义为IIdentity的事实是否会以某种方式阻止使用实现类型的显式运算符解决转换? 这会让我感到奇怪。

更新(#2)

我觉得我发布了所有这些更新的垃圾邮件(也许我应该让我一起行动才能让触发得快乐:))无论如何,我修改了我的Foo的GetCoreIdentityMethod来代替:

 public CoreIdentity GetCoreIdentity() { ExtendedIdentity exId = Identity as ExtendedIdentity; if (exId != null) return (CoreIdentity)exId; return (CoreIdentity)Identity; } 

(在必须清理由两个类中的运算符引起的模糊引用之后),它确实进入了我的显式转换运算符代码,并且它按预期工作。 所以我觉得看起来像显式运算符没有多态解析(正确理解?),而且我的属性被输入为IIdentity而不是ExtendedIdentity的事实阻止了它调用转换逻辑,即使它是调用时的ExtendedIdentity类型。 这让我觉得非常特别和意外……而且有点不幸。

我不想重写CurrentIdentity对象的守护者,让它知道我的特殊测试演员模拟。 我想将这个“特殊”逻辑封装到mock本身中,所以这真的让我想到了一个循环。

因为不调用转换运算符。 请参阅: http : //msdn.microsoft.com/en-us/library/cscsdfbt(v = VS.100).aspx

使用(演员)。

我将我的身份(在Foo中)定义为IIdentity的事实是否会以某种方式阻止使用实现类型的显式运算符解决转换?

这是一个提示:如何定义显式(或隐含的)转换运算符? (我知道你知道这个,因为你已经做过了;我问这个问题是为了说明一点。)

 public static explicit operator UserIdentity(MockUserIdentity src) { return src.UserIdentity; } 

这里有一些非常重要的事情要做。 C#设计师明智地选择让所有操作员都保持静态 。 因此,上面定义的显式运算符实际上转换为静态方法调用,如下所示:

 public static UserIdentity op_Explicit(MockUserIdentity src) { return src.UserIdentity; } 

现在,这就是我所要做的。 在你的问题中困扰你的行为,因为它似乎在多态性部门失败,实际上是C#的方法重载系统解决方案的结果。

如果我有两种方法:

 void Write(string s) { Console.WriteLine("string"); } void Write(object o) { Console.WriteLine("object"); } 

……然后我有这个程序:

 object x = "Hello!"; Write(x); 

输出会是什么?

答案是“对象”,因为编译器选择了Write(object)重载 – 它应该是。 Write不是根据正常多态性被某些派生类型覆盖的实例方法; 它是一个静态方法,具有重载,编译器必须在这些重载之间做出选择。 由于上述代码中的x被声明为object类型,因此该选择是明确的Write(object)

所以对于你的代码,你有这个:

 public IIdentity Identity { get; set; } public CoreIdentity GetCoreIdentity() { return (CoreIdentity)Identity; } 

编译器必须调查:是否存在接受op_Explicit参数的op_Explicit重载? 不,那里没有。 有一个接受UserIdentity参数,但这太具体了(正如上面的例子中的Write(string)对于x来说太具体了)。

因此,在初始测试中未调用显式运算符的原因是编译器不会将(CoreIdentity)Identity解析为该特定重载。 这也是您的修改版本可行的原因:

 public CoreIdentity GetCoreIdentity() { ExtendedIdentity exId = Identity as ExtendedIdentity; if (exId != null) { // Since exId is actually declared to be of type ExtendedIdentity, // the compiler can choose the operator overload accepting // an ExtendedIdentity parameter -- so this will work. return (CoreIdentity)exId; } return (CoreIdentity)Identity; } 

那么,你为什么不使用一个明确的演员?

 // will throw if cast fails internal static UserIdentity GetCurrentIdentity() { UserIdentity currentIdentity = (UserIdentity) ApplicationContext.User.Identity ; return currentIdentity; } 

这应该触发你的显式运算符。 您可以先测试,以使其更安全。

正如Ray所提到的as ,不会调用转换运算符。

也就是说,您应该在该类型的场景中使用显式转换。

这样,当没有正确设置某些内容并且ApplicationContext.User.Identity中的对象不是代码所期望的那样时,您会获得非常清晰的信息。