【问题标题】:Why am I receiving an InvalidCastException here?为什么我在这里收到 InvalidCastException?
【发布时间】:2019-09-09 10:53:33
【问题描述】:

我有以下两个类:

    public class Foo : IFooOperations
    {
        public FooData Data { get; set; }
        public FooUser User { get; set; }

        public Foo(FooData fooData)
        {
            Data = fooData;
        }

        private async void SetupFoo()
        {
            ...
        }

        public async void FooOp()
        {
            ...
        }
    }

    public class Bar : Foo, IBarOperations
    {

        public Foo(FooData fooData) : base(fooData)
        {
        }

        public async void BarOp()
        {
            ...
        }
    }

在我获得Foo 对象后,为什么Bar bar = (Bar)foo; 不允许我将其转换为Bar 对象?它会引发 InvalidCastException: Specified cast is not valid. 异常。

在我看来,这应该可行。 Bar 只是 Foo 有一个额外的方法对两个对象已经通用的状态进行操作。

一些额外的上下文:Bar 对象本质上是特权Foo 用户,根据他们的数据,他们被允许访问BarOp() 方法。我正在构建一个 SDK,并希望(ab)使用强制转换机制来默认隐藏这些特权方法。我希望我的最终用户需要有意识地决定 Foo 确实是 Bar 并在他们能够访问 BarOp() 方法之前执行转换。

是否有人对可能出现的问题、我如何解决此问题或要考虑的替代架构有任何想法?

【问题讨论】:

  • 这与 Bar 也实现了 IBarOperations 的事实有关,这不是 Foo 的事情
  • 顺便说一句,您实际上有很多异步 void 方法吗?我强烈建议不要这样做 - 如果可能的话,让他们返回 Task。我意识到这可能实际上不能代表您的代码。
  • 啊,谢谢!回想起来,在这里错过似乎是一件愚蠢的事情。 @JonSkeet 别担心,这些实际上都是任务。 :)

标签: c# inheritance interface casting


【解决方案1】:

Bar 只是 Foo,带有一个额外的方法对两个对象已经通用的状态进行操作。

这与演员表是否有效完全无关。只有当引用的对象 目标类型的实例或某些子类型时,强制转换(在引用类型之间,忽略用户定义的转换)才能在 .NET 中成功。就是这么简单。你可以有两个没有任何额外成员的类:

class Base {}
class Derived : Base {}

...如果您在实际拥有Base 的实例时尝试转换为Derived,则转换仍然会失败:

Base b = new Base();
Derived d = (Derived) b; // Exception

我希望我的最终用户需要有意识地决定 Foo 确实是 Bar 并在他们能够访问 BarOp() 方法之前执行转换。

但对象不是实际上是Bar,否则演员表会起作用。如果您确保在任何时候都创建了实际Bar 对象,那么您的计划将是合理的。

如果您不想添加任何执行时安全性,只需让开发人员说“我真的打算这样做”,那么您可以编写一个包装器类型:

class Bar : IBarOperations
{
     // Keep a reference to the existing Foo object
     // Expose privileged methods         
}

那么如果你想使用流畅的风格,你可以为AsBar()Foo编写一个实例或扩展方法,构造一个新的Bar,导致代码为:

foo.AsBar().BarOp();

这仍然保持额外的“检查”(即使它确实不是验证用户应该能够做到这一点)而不尝试使用强制转换。

【讨论】:

  • 感谢您抽出宝贵时间来说明这一点,这对我来说真的很清楚。 AsBar() 方法似乎可以满足我的要求。我完全同意这不是任何形式的真正验证。我依靠来自远程服务器的异常和错误代码系统(实际上是在执行此操作)来最终防止误用。但是能够将这些方法分解到另一个类中对我来说似乎是值得的,以免所有的 Foos 都携带 99% 的方法实际上无法使用。
猜你喜欢
  • 1970-01-01
  • 2020-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多