关于“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中的对象不是代码所期望的那样时,您会获得非常清晰的信息。