【问题标题】:Threadsafe Generic Extension method usage syntax issueThreadsafe Generic Extension 方法使用语法问题
【发布时间】:2009-07-03 13:40:51
【问题描述】:

这是我在控件上调用的扩展方法:

public static void Invoke<T>(this T c, Action<System.Windows.Forms.Control> DoWhat)
    where T:System.Windows.Forms.Control
{
    if (c.InvokeRequired)
        c.Invoke(o=> DoWhat(c) );
    else
        DoWhat(c);
}

ds 是一个强类型数据集。 这有效:

Action<DataGridView> a = row => row.DataSource = ds.bLog;
this.dataGridView1.Invoke(a);

这不会编译:

this.dataGridView1.Invoke<DataGridView>(o => o.DataSource = ds.bLog);

并说 System.Windows.Forms.Control 不包含“数据源”的定义...

我真的必须把它分成两行吗? 为了清楚/安全,我应该调用通用扩展方法 InvokeSafe 吗?

编辑:扩展方法已修改(有效,但我想删除命名委托要求):

private delegate void myDel();

public static void InvokeSafe<T>(this T c, Action<T> DoWhat) where T : Control
{
    myDel d = delegate() { DoWhat(c); };
    if (c.InvokeRequired)
        c.Invoke(d);
    else
        DoWhat(c);
}

我似乎无法弄清楚如何将myDel 分解为块中的匿名委托?

【问题讨论】:

  • 显然我在 c.Invoke(o=> DoWhat(c) );因为它是递归方法,而不是调用控件的 .Invoke(delegate) 方法。

标签: c# multithreading generics user-interface extension-methods


【解决方案1】:

问题在于您的操作仅被声明(在方法中)以作用于Control。改成这样:

public static void Invoke<T>(this T c, Action<T> DoWhat)
    where T:System.Windows.Forms.Control
{
    if (c.InvokeRequired)
        c.Invoke((EventHandler) delegate { DoWhat(c) } );
    else
        DoWhat(c);
}

这样编译器会推断出您需要 Action&lt;DataGridView&gt;,因此调用者中的 lambda 表达式将能够使用 DataSource

【讨论】:

  • c.Invoke(o=> DoWhat(c) );在我看来是一个堆栈溢出,当我将函数名称更改为 InvokeSafe 时,该行不再起作用。我正在尝试调用控件的 Invoke,而不是递归。
  • 为什么会出现堆栈溢出?请记住,我更改的代码only 部分是 DoWhat 变量的类型。
  • 因为我的代码一开始就不好。它以书面形式调用自身,而不是控件的调用方法。
  • 这是在哪里做的?或者你的意思是你没有发布的代码?
  • 原文中的那一行和你的答案。 c.Invoke(o=> DoWhat(c) );有没有办法订阅 cmets 来回答您的问题,或者评论对您自己的 cmets 的回复?
【解决方案2】:

尝试将您的方法签名更改为:

public static void Invoke<T>(this T c, Action<T> DoWhat)

这将使您的Action 使用您指定的所需类型。

【讨论】:

  • 谢谢,这是我丢失的关键,结合将 Invoke 更改为 InvokeSafe 以查找意外堆栈溢出,谢谢。如果我没看错,你在乔恩之前就得到了答案,并且可以更多地使用代表,所以我会接受这个。
  • 作为一个程序问题,我的回答是在 Jason 的前 2 分钟,但我真的不介意 :)
  • @Jon - 我希望你不会介意,我想我是倒着读的,谢谢 Jon
【解决方案3】:

为了进一步说明 Jon 所说,数据源不在 System.Windows.Forms.Control 上。数据源位于数据网格上。因此,如果您查看错误消息,您将看到它正在尝试将数据源设置为没有数据源的类型。按照 Jon 的建议去做应该可以解决问题。

【讨论】:

    【解决方案4】:

    有效!

    public static void InvokeSafe<T>(this T c, Action<T> DoWhat)
    where T : System.Windows.Forms.Control
        {
    
            Action d = delegate() { DoWhat(c); };
            if (c.InvokeRequired)
                c.Invoke(d);
            else
                DoWhat(c);
    
        }
    

    此代码确实会导致 InvalidOperationException - 跨线程操作无效:

    List<customer> c=new List<customer>{new customer{ Name="test"},new customer{Name=   "John doe"}};
            Thread t = new Thread(o => this.dataGridView1.DataSource = c);
            t.Start();
    

    新的扩展方法没有!

    List<customer> c=new List<customer>{new customer{ Name="test"},new customer{Name=   "John doe"}};
            Thread t = new Thread(o => this.dataGridView1.InvokeSafe(p => p.DataSource = c));
            t.Start();
    

    【讨论】:

    • 我想也有一种方法可以排除显式委托初始化,但我很满意。
    猜你喜欢
    • 2017-01-03
    • 2021-01-03
    • 1970-01-01
    • 1970-01-01
    • 2011-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多