【问题标题】:Casting a Delegate to a Generic delegate in C#在 C# 中将委托强制转换为通用委托
【发布时间】:2016-10-02 20:13:28
【问题描述】:

简介

我正在使用委托来传递和存储单个表单Controls 的样式逻辑。例如,我有一个包含一些Button-样式逻辑的委托,如下所示:

button.BackColor = Color.Red;
button.ForeColor = Color.White;
button.FlatStyle = FlatStyle.Flat;

当然还有许多不同类型的控件,例如标签、面板等。所以为了存储所有这些委托,我使用了Dictionary<Type, Delegate>

虽然,委托本身看起来像这样:

delegate void StyleDel<in T>(T control) where T : Control;

因此,为了使用字典中的逻辑,必须首先将 Delegate 转换为 StyleDel&lt;T&gt; - 无论此时 T 可能是什么。


情况

在初始化并存储所有样式后,必须应用样式(使用StyleDels)。为此,我创建了一个函数StyleControl(control)

此函数查看控件的类型(例如Button)并从Dictionary 中找到相应的StyleDel,然后应用 (Button-) 样式。

public void StyleControl<T>(T control) where T : Control
{
    Delegate storedDel;
    if (_dict.TryGetValue(control.GetType(), out storedDel))
    {
        // Cast Delegate to StyleDel
        var styleDel = (StyleDel<T>) storedDel;

        // Execute StyleDel
        styleDel(control);
    }
}

StyleDels 使用下面的Add 函数添加到字典中:

public bool Add<T>(StyleDel<T> styleDel) where T : Control
{
    var inDict = _dict.ContainsKey(typeof(T)); 
    if (!inDict) _dict[typeof(T)] = styleDel;
    return !inDict;
}

StyleControl 函数被另一个函数调用,这确保了所有内容都是递归的:

public void Style<T>(T parent) where T : Control
{
    StyleControl(parent);

    // The problem might have to do with this
    foreach (Control child in parent.Controls) Style(child);
}

问题

抛出InvalidCastException,表示StyleDel&lt;Button&gt; 无法转换为StyleDel&lt;Control&gt;。所以我相信这是说T 在这一点上被视为Control,而实际上它是Button

如何成功将此Delegate 转换为StyleDel&lt;Button&gt;

【问题讨论】:

  • 你能把代理第一次添加到字典中的代码贴出来吗?
  • 你是怎么调用这个方法的?无论是隐式还是显式,您都使用T 作为Control 调用。
  • 我会说问题在于您将委托添加到字典的位置,例如,以下内容有效:_dict.Add(typeof(Button), (StyleDel&lt;Button&gt;)((Button b) =&gt; b.AutoEllipsis = true));
  • @Abion47 我已将该代码放入问题中
  • @Duncan 是的,就是这样。您得到的结果与将泛型替换为 Control 相同。在不使用反射调用委托的情况下如何解决它并不是很明显。

标签: c# generics casting delegates


【解决方案1】:

您可以通过添加一定程度的间接性来实现这一点;创建一个 lambda,调用您的委托将参数转换为正确的类型:

Dictionary<Type, StyleDel<Control>> _dict = ...

public bool Add<T>(StyleDel<T> styleDel) where T : Control
{
    var inDict = _dict.ContainsKey(typeof(T)); 
    if (!inDict) _dict[typeof(T)] = d => StyleDel((T)d);
    return inDict;
}

乍一看,这似乎不是类型安全的,但在这种特殊情况下,这是因为委托存储在字典中,参数的真实类型作为它的键。因此,预期用途将始终确保始终使用类型正确的参数调用委托,并且不会发生运行时强制转换异常。

【讨论】:

  • @CharlesMager 不,因为字典现在是Dictionary&lt;Type, StyleDel&lt;Control&gt;&gt;。忘了添加那一点,编辑过的答案。
  • 那么您仍然遇到StyleDel&lt;Button&gt; 不是StyleDel&lt;Control&gt; 的问题。这不会编译。
  • @CharlesMager 这编译并运行得很好。您只是得到一个MyDel&lt;Button&gt; 并将其存储为MyDel&lt;Control&gt;,并带有一个额外的调用和一个始终会成功的参数转换。添加到字典时的 Lamba 参数 d 是隐式类型的 Control 因为那是字典的类型。您只需将其转换为正确的类型并调用提供的委托。它是万无一失的。
  • 啊,好吧,对不起 - 我错过了 d 实际上是 Control 在这种情况下。你失去了一些类型安全,如果你用Button以外的任何东西调用StyleDel&lt;Control&gt;,那么你将在将Control转换为Button时遇到运行时异常-你也可以删除来自StyleDel&lt;T&gt; 的通用T - 但我想它在这种情况下确实有效! +1
  • @CharlesMager 不,您不想删除 T,因为这正是强制将 StyleDel&lt;Button&gt;typeof(Button) 键一起存储的原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多