我正在尝试对一个不幸具有高度单位相互依赖性的项目进行一些单元测试.目前,我们的很多类都在寻找自定义UserIdentity对象来确定身份验证,但是该对象有很多内部环绕跳跃,我在试图测试单个单元功能时会尽快避免.
为了解决其中一些问题,我正在尝试创建一个UserIdentity的"模拟"版本,可以通过更严格控制的变量环境插入.
简而言之,我们有一个UserIdentity类,它有几个公共只读属性和一个静态CurrentIdentity(IIdentity)占位符.我可以通过"模拟" IIdentity实现来解决所有问题,但是当我达到将CurrentIdentity作为UserIdentity进行投射时,我遇到了障碍.
这是一个非常简单的方法:
internal static UserIdentity GetCurrentIdentity()
{
UserIdentity currentIdentity = ApplicationContext.User.Identity as UserIdentity;
return currentIdentity;
}
Run Code Online (Sandbox Code Playgroud)
我已经设置了我的模拟对象来创建UserIdentity类型的成员,然后执行以下操作:
public static implicit operator UserIdentity(MockUserIdentity src)
{
return src.UserIdentity;
}
Run Code Online (Sandbox Code Playgroud)
或这个
public static explicit operator UserIdentity(MockUserIdentity src)
{
return src.UserIdentity;
}
Run Code Online (Sandbox Code Playgroud)
问题是,据我所知,'as'似乎没有在我的模拟对象上调用隐式或显式转换操作.我的问题是(是吗?),我在这里遗漏了一些简单的东西,或者这不起作用,因为(我猜)'as'操作直接看到类继承(我的对象不做...) ?
此外,可能有点偏离主题,但为什么在类中不能同时使用相同结果类型的显式和隐式运算符?除非我遗漏了一些愚蠢的东西,否则如果我尝试同时拥有两个转换运算符,编译器就会黯然失色.我必须选择一个或另一个.
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();
}
}
Run Code Online (Sandbox Code Playgroud)
但是当我调用foo.GetCoreIdentity()时会抛出以下异常:
无法将"ExtendedIdentity"类型的对象强制转换为"CoreIdentity".
并且我无法使用断点捕获任何一个明确的运算符,因此看起来它甚至没有"尝试"我提供的转换路径就做出了这个决定.
当然我错过了一些明显的东西.我将我的身份(在Foo中)定义为IIdentity的事实是否会以某种方式阻止使用实现类型的显式运算符解决转换?这会让我感到奇怪.
更新(#2)
我觉得我发布了所有这些更新的垃圾邮件(也许我应该让我一起行动才能让触发得开心:))无论如何,我修改了我的Foo的GetCoreIdentityMethod来代替:
public CoreIdentity GetCoreIdentity()
{
ExtendedIdentity exId = Identity as ExtendedIdentity;
if (exId != null)
return (CoreIdentity)exId;
return (CoreIdentity)Identity;
}
Run Code Online (Sandbox Code Playgroud)
(在必须清理由两个类中的运算符引起的模糊引用之后),它确实进入了我的显式转换运算符代码,并且它确实按预期工作.所以我认为显式运算符看起来不是多态解析的(正确的理解是什么?),而且我的属性被输入为IIdentity而不是ExtendedIdentity这一事实阻止了它调用转换逻辑,即使它是调用时的ExtendedIdentity类型.这让我觉得非常特别和意想不到......而且有点不幸.
我不想重新编写CurrentIdentity对象的守护者,以使其了解我的特殊测试演员模拟.我想将这个"特殊"逻辑封装到mock本身中,所以这真的让我想到了一个循环.
我将我的身份(在Foo中)定义为IIdentity的事实是否会以某种方式阻止使用实现类型的显式运算符解决转换?
这是一个提示:如何定义显式(或隐含的)转换运算符?(我知道你知道这个,因为你已经做过了;我问这个问题是为了说明一点.)
public static explicit operator UserIdentity(MockUserIdentity src)
{
return src.UserIdentity;
}
Run Code Online (Sandbox Code Playgroud)
这里有一些非常重要的事情要做.C#设计师明智地选择了让所有运营商保持静态.因此,上面定义的显式运算符实际上转换为静态方法调用,如下所示:
public static UserIdentity op_Explicit(MockUserIdentity src)
{
return src.UserIdentity;
}
Run Code Online (Sandbox Code Playgroud)
现在,这就是我要做的.在你的问题中困扰你的行为,因为它似乎在多态性部门失败,实际上是C#的方法重载系统解决方案的结果.
如果我有两种方法:
void Write(string s) { Console.WriteLine("string"); }
void Write(object o) { Console.WriteLine("object"); }
Run Code Online (Sandbox Code Playgroud)
......然后我有这个程序:
object x = "Hello!";
Write(x);
Run Code Online (Sandbox Code Playgroud)
输出会是什么?
答案是"对象",因为Write(object)编译器选择了重载 - 它应该是.Write不是根据正常多态性被某些派生类型覆盖的实例方法; 它是一个静态方法,具有重载,编译器必须在这些重载之间做出选择.由于x上面的代码被声明为类型object,因此该选择是明确的Write(object).
所以对于你的代码,你有这个:
public IIdentity Identity { get; set; }
public CoreIdentity GetCoreIdentity()
{
return (CoreIdentity)Identity;
}
Run Code Online (Sandbox Code Playgroud)
编译器必须调查:是否存在op_Explicit接受IIdentity参数的重载?不,那里没有.有一个接受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;
}
Run Code Online (Sandbox Code Playgroud)