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

时间:2010-10-06 15:06:31

标签: c# .net casting

我正在尝试对一个不幸具有高度单位相互依赖性的项目进行一些单元测试。目前,我们的许多课程都在寻找自定义 UserIdentity 对象来确定身份验证,但是该对象有很多内部环绕跳跃,我在试图测试单个单元功能时会尽快避免。

要解决其中一些问题,我正在尝试创建此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'操作直接看到类继承(我的对象不做...) ?

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

更新

好的,现在我完全糊涂了。也许我变得邋,,但我已经尝试过做直接演员,我似乎无法让它发挥作用。我在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()时会抛出以下异常:

无法将“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对象的守护者,以使其了解我的特殊测试演员模拟。我想将这种“特殊”逻辑封装到模拟本身中,所以这真的让我失去了一个循环。

4 个答案:

答案 0 :(得分:5)

  

我是否拥有自己的身份   (在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);

输出结果是什么?

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

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

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

编译器必须调查:是否存在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;
}

答案 1 :(得分:3)

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

使用(演员)。

答案 2 :(得分:1)

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

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

这应该触发你的显式操作符。您可以先使用is进行测试,以使其更安全。

答案 3 :(得分:0)

如Ray as所述,不会调用转换运算符。

那就是说,你应该在这种场景中使用显式强制转换。

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