【问题标题】:Resolving dynamic types in .NET 4 vs SL 5解析 .NET 4 与 SL 5 中的动态类型
【发布时间】:2012-09-12 01:23:15
【问题描述】:

我在 Silverlight 5 中有以下代码:

public void Send(Notification notification)
{
    // Because the variable is passed as Notification, we have to trick the
    // type-inference feature of the runtime so the message will be sent using
    // the actual type of the object.
    // Using the dynamic keyword, we can convert the type of the object passed
    // to the Send method.
    // This allows subscribers to register for messages using the generic interface.
    dynamic stronglyTypedNotification = Convert.ChangeType(notification,
                                                           notification.GetType(),
                                                           CultureInfo.InvariantCulture);

    SendCore(stronglyTypedNotification);
}

private void SendCore<T>(T notification)
    where T : Notification
{
    foreach (var handlerFactory in Handlers)
    {
        var handler = handlerFactory.Value as INotificationHandler<T>;

        if (handler != null)
        {
            handler.Handle(notification);
        }
    }
}

我必须将此代码移植到 WPF 应用程序中运行。

当我在 WPF 应用程序中运行它并在 SendCore 方法中设置断点时,T 不是正确的类型。我只能假设这是因为泛型应该是静态定义的,因此编译器创建了它认为在运行时需要的 SendCore 版本。我猜 Silverlight 以不同的方式处理这个问题,因为这段代码在 SL 中完美运行。

此代码的目标是定位 Handlers 集合中包含的任何实现 INotificationHandler 的对象,其中 T 是传递给 Send 方法的对象的类型(继承基本 Notification 类)然后调用 Handle(T n ) 方法。

如何在我的 WPF 应用程序中执行此操作?

更新

经过一些额外的测试,我发现了更奇特的结果。当我在 SendCore 方法的第一行设置断点并检查 T 和通知时,我发现:

  • Intellisense 指示 T 是“GenericNotification”,即 程序集中的合法类。
  • Intellisense 表示“通知”是正确的, 强类型类 (DataChangedNotification)。
  • DataChangedNotification 不在任何子类中的 GenericNotification 大大地。都是通知的子类。
  • 因为 T 正在解析 GenericNotification,所以尝试强制转换 INotificationHandler 的每个处理程序都会失败,因为没有 实现 INotificationHandler

鉴于此确切代码在 Silverlight、一个控制台应用程序和另一个 WPF 应用程序(以及 David 在 LINQpad 中的测试)中工作,到底会发生什么?

我能想到的唯一一件事是代码实际上存在于 WPF 应用程序引用的类库中。不确定这是否重要,因为我在另一个(新)WPF 应用程序中测试了该场景并得到了正确的结果。

【问题讨论】:

  • 当你说“这是错误的类型”时,它是什么类型?
  • 顺便说一句:这是丹尼尔,不是大卫。从您的更新中,我假设您只是传递了错误的变量或查看了错误的代码!当 DataChangedNotification 是类层次结构中的同级时,不可能将 DataChangedNotification 作为 GenericNotification 传递。

标签: .net generics dynamic


【解决方案1】:

它对我有用,这是意料之中的。如果它不一样,我会感到惊讶。

void Main()
{
    Send(new NA());
    Send(new NB());
}

public class Notification {}
public class NA : Notification {}
public class NB : Notification {}

public void Send(Notification notification)
{
    dynamic stronglyTypedNotification
        = Convert.ChangeType(notification,
                             notification.GetType(),
                             CultureInfo.InvariantCulture);

    SendCore(stronglyTypedNotification);
}

public void SendCore<T>(T notification) where T : Notification
{
    Console.WriteLine(typeof(T));
}

这个输出

typeof (NA) 
typeof (NB)

【讨论】:

  • 我没有得到这个结果。 (您可能希望使用实际输出更新您的帖子,因为显示的内容不是您的代码生成的内容。)无论我将类型传递给 Send 方法,typeof(T) 始终返回 GenericNotification。
  • 几件事使它更具可比性:1)使通知抽象。 2) 将所有通知代码(除 Main 之外的所有代码)放入从 WPF 应用程序引用的类库中。 3) 虽然我相信检查 typeof(T) 将表明其余代码是否可以工作,但请记住,我正在尝试使用该类型将对象强制转换为 INotificationHandler。对于真正准确的测试,该部分也应该被复制。不确定这是否会有所作为,但它会使您的测试更加完整。
  • @SonOfPirate:可能应该从我的代码开始并改变一些东西,直到它不再像你期望的那样工作。根据这些信息,您应该提出一个新问题。
  • 根据该屏幕截图,您没有在 WPF 应用程序中测试代码。在 LINQPad 中运行它不是一回事。在实际的 .NET 应用程序中,Console.WriteLine(typeof(T)) 将导致写出类型信息。 LINQPad 内部发生了一些“隐藏”的逻辑。将此代码放入控制台应用程序中,您会看到类似 ConsoleApplication1.NA 和 ConsoleApplication1.NB 的内容。
  • @SonOfPirate:停止争论,开始测试。 LINQPad 或 WPF,一点都不重要。发生的类型推断是相同的,您的控制台应用程序清楚地表明了这一点。你也得到NANB 而不是Notification。我非常确定您的问题完全是另外一回事 - 因此您需要检查 your 代码。类型推断按预期工作。
【解决方案2】:

尽管存在明显的性格冲突,但我还是要感谢 Daniel 推动我继续深入挖掘问题。我同时要求将其删除,因为我认为它对其他人没有任何价值。

事实证明,问题确实源自其他地方,并且被简单的 ToString() 重载所掩盖。

第一个问题:我看到(和报告)的行为是另一个开发人员帮助添加 ToString 覆盖到基 Notification 类的结果,该类在调试器中显示对象的名称而不是数据类型。这就是为什么在 VS 中查看“通知”参数会显示预期的类型名称。实际上,传入的对象类型是 GenericNotification。

一旦我意识到发生了这种情况,我就能够打开我们的框架代码(独立构建的单独解决方案)来查看对象的实例化位置。在那里我发现该文件的 .NET 版本的实现方式与 Silverlight 版本不同。我们使用 MEF 进行依赖注入,并且 Silverlight 版本的代码根据导入的受支持类型列表解析数据类型。 .NET 版本使用了 switch 语句(啊!)。

所以,我更改了 .NET 版本以使用 MEF 动态创建对象来帮助解析类型和中提琴!现在一切都按预期工作,压力也得到了缓解(这是我们软件的一个关键功能,所以让它不起作用......)。

【讨论】:

  • 找到那个真好!很抱歉让我这么生气,但我只是知道这是另一回事,如果你继续和我争吵,你永远不会知道 :-) 如果你想删除你的问题标记它以引起版主注意。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-27
  • 2020-08-13
  • 2018-12-29
  • 1970-01-01
相关资源
最近更新 更多