C# 当作为普通委托参数提供时,为什么必须强制转换lambda表达式

C# 当作为普通委托参数提供时,为什么必须强制转换lambda表达式,c#,c#-3.0,delegates,lambda,C#,C# 3.0,Delegates,Lambda,采用方法System.Windows.Forms.Control.Invoke(委托方法) 为什么会出现编译时错误: string str = "woop"; Invoke(() => this.Text = str); // Error: Cannot convert lambda expression to type 'System.Delegate' // because it is not a delegate type 但这很管用: string str = "woop"; I

采用方法System.Windows.Forms.Control.Invoke(委托方法)

为什么会出现编译时错误:

string str = "woop";
Invoke(() => this.Text = str);
// Error: Cannot convert lambda expression to type 'System.Delegate'
// because it is not a delegate type
但这很管用:

string str = "woop";
Invoke((Action)(() => this.Text = str));

当方法需要普通委托时?

lambda表达式可以转换为委托类型或表达式树,但它必须知道哪种委托类型。仅仅知道签名是不够的。例如,假设我有:

public delegate void Action1();
public delegate void Action2();

...

Delegate x = () => Console.WriteLine("hi");
您希望
x
所指对象的具体类型是什么?是的,编译器可以生成具有适当签名的新委托类型,但这很少有用,并且最终错误检查的机会更少

如果您想使调用
控件变得简单。使用
操作调用
,最简单的方法是为控件添加扩展方法:

public static void Invoke(this Control control, Action action)
{
    control.Invoke((Delegate) action);
}

厌倦了一次又一次地投兰姆达斯

public sealed class Lambda<T>
{
    public static Func<T, T> Cast = x => x;
}

public class Example
{
    public void Run()
    {
        // Declare
        var c = Lambda<Func<int, string>>.Cast;
        // Use
        var f1 = c(x => x.ToString());
        var f2 = c(x => "Hello!");
        var f3 = c(x => (x + x).ToString());
    }
}
公共密封类Lambda
{
公共静态Func Cast=x=>x;
}
公开课范例
{
公开募捐
{
//申报
var c=Lambda.Cast;
//使用
var f1=c(x=>x.ToString());
var f2=c(x=>“你好!”);
var f3=c(x=>(x+x).ToString();
}
}

有十分之九的人会因为试图封送到UI线程而收到此消息。以下是懒惰的方式:

static void UI(Action action) 
{ 
  System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(action); 
}
既然已经键入,问题就消失了(qv Skeet's anwer),我们有了这个非常简洁的语法:

int foo = 5;
public void SomeMethod()
{
  var bar = "a string";
  UI(() =>
  {
    //lifting is marvellous, anything in scope where the lambda
    //expression is defined is available to the asynch code
    someTextBlock.Text = string.Format("{0} = {1}", foo, bar);        
  });
}
关于奖励积分,这里有另一个提示。对于UI内容,您不会这样做,但是如果您需要某种方法来阻止它,直到它完成(例如请求/响应I/O,等待响应),请使用(qv-msdn-WaitAll,WaitAny,WaitOne)

请注意,AutoResteEvent是一个WaitHandle派生

public void BlockingMethod()
{
  AutoResetEvent are = new AutoResetEvent(false);
  ThreadPool.QueueUserWorkItem ((state) =>
  {
    //do asynch stuff        
    are.Set();
  });      
  are.WaitOne(); //don't exit till asynch stuff finishes
}
还有最后一个提示,因为事情可能会变得复杂:WaitHandles会使线程暂停。这就是他们应该做的。如果在UI线程暂停时尝试封送到该线程,则应用程序将挂起。在这种情况下,(a)需要进行一些严重的重构,(b)作为一种临时的黑客,您可以像这样等待:

  bool wait = true;
  ThreadPool.QueueUserWorkItem ((state) =>
  {
    //do asynch stuff        
    wait = false;
  });
  while (wait) Thread.Sleep(100);

彼得·沃恩。你是达曼。 让你的概念更进一步,我提出了这两个函数

private void UIA(Action action) {this.Invoke(action);}
private T UIF<T>(Func<T> func) {return (T)this.Invoke(func);}
private void UIA(Action-Action){this.Invoke(Action);}
私有tuif(Func Func){返回(T)this.Invoke(Func);}
我把这两个函数放到我的表单应用程序中,我可以像这样从后台工作人员那里打电话

int row = 5;
string ip = UIF<string>(() => this.GetIp(row));
bool r = GoPingIt(ip);
UIA(() => this.SetPing(i, r));
this.BeginInvoke((Action)delegate {
    // do awesome stuff
});
int行=5;
字符串ip=UIF(()=>this.GetIp(行));
bool r=GoPingIt(ip);
UIA(()=>this.SetPing(i,r));
可能有点懒,但我不必设置worker-done函数, 这在这种情况下非常方便

private void Ping_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
  int count = this.dg.Rows.Count;
  System.Threading.Tasks.Parallel.For(0, count, i => 
  {
    string ip = UIF<string>(() => this.GetIp(i));
    bool r = GoPingIt(ip);
    UIA(() => this.SetPing(i, r));
  });
  UIA(() => SetAllControlsEnabled(true));
}
private void Ping\u DoWork(对象发送方,System.ComponentModel.DoWorkEventArgs e)
{
int count=this.dg.Rows.count;
System.Threading.Tasks.Parallel.For(0,count,i=>
{
字符串ip=UIF(()=>this.GetIp(i));
bool r=GoPingIt(ip);
UIA(()=>this.SetPing(i,r));
});
UIA(()=>SetAllControlsEnabled(true));
}

本质上,从gui DataGridView获取一些ip地址,ping它们,将生成的图标设置为绿色或红色,并在表单上重新启用按钮。是的,这是一个“平行。为”在后台工作。是的,这是一个很大的调用开销,但对于短列表和更紧凑的代码来说可以忽略不计

我试图把这个建立在他的答案之上。也许这只是一个小小的进步

public sealed class Lambda<S>
{
    public static Func<S, T> CreateFunc<T>(Func<S, T> func)
    {
        return func;
    }

    public static Expression<Func<S, T>> CreateExpression<T>(Expression<Func<S, T>> expression)
    {
        return expression;
    }

    public Func<S, T> Func<T>(Func<S, T> func)
    {
        return func;
    }

    public Expression<Func<S, T>> Expression<T>(Expression<Func<S, T>> expression)
    {
        return expression;
    }
}
在同一个类中,同样可以为
操作
表达式
添加额外的重载。对于其他内置委托和表达式类型,必须编写单独的类,如
Lambda
Lambda
Lambda

与原始方法相比,我看到了这一方法的优势:

  • 少一个类型规范(只需指定形式参数)

  • 这使您可以自由地对任何
    Func
    使用它,而不仅仅是在
    T
    表示为
    string
    时,如示例所示

  • 直接支持表达式。在前面的方法中,必须再次指定类型,如:

    var e = Lambda<Expression<Func<int, string>>>.Cast(x => "Hello!");
    
    //or in case 'Cast' is an instance member on non-generic 'Lambda' class:
    var e = lambda.Cast<Expression<Func<int, string>>>(x => "Hello!");
    
    因此,情况归结为:

    var l = Lambda<int>.Expression;
    var e1 = l(x => x.ToString());
    var e2 = l(x => "Hello!");
    var e3 = l(x => x + x);
    
    var l=Lambda.Expression;
    var e1=l(x=>x.ToString());
    var e2=l(x=>“你好!”);
    变量e3=l(x=>x+x);
    

    这就更不用打字了,但是你会失去某些类型安全性,而且在我看来,这是不值得的。

    晚会有点晚,但你也可以这样施放

    int row = 5;
    string ip = UIF<string>(() => this.GetIp(row));
    bool r = GoPingIt(ip);
    UIA(() => this.SetPing(i, r));
    
     this.Dispatcher.Invoke((Action)(() => { textBox1.Text = "Test 123"; }));
    
    this.BeginInvoke((Action)delegate {
        // do awesome stuff
    });
    
    通过使用XUnit,我发现使用这种内联功能非常酷

    之前

    [Fact]
    public void Pass_Open_Connection_Without_Provider()
    {
        Action action = () => {
            using (var c = DbProviderFactories.GetFactory("MySql.Data.MySqlClient").CreateConnection())
            {
                c.ConnectionString = "<xxx>";
                c.Open();
            }
        };
    
        action.Should().Throw<Exception>().WithMessage("xxx");
    }
    
    [事实]
    公共无效通过\打开\连接\无\提供程序()
    {
    动作动作=()=>{
    使用(var c=dbProviderFactorys.GetFactory(“MySql.Data.MySqlClient”).CreateConnection()
    {
    c、 ConnectionString=“”;
    c、 Open();
    }
    };
    action.Should().Throw().WithMessage(“xxx”);
    }
    
    之后

    [Fact]
    public void Pass_Open_Connection_Without_Provider()
    {
        ((Action)(() => {
            using (var c = DbProviderFactories.GetFactory("<provider>").CreateConnection())
            {
                c.ConnectionString = "<connection>";
                c.Open();
            }
        })).Should().Throw<Exception>().WithMessage("Unable to find the requested .Net Framework Data Provider.  It may not be installed.");
    }
    
    [事实]
    公共无效通过\打开\连接\无\提供程序()
    {
    ((行动)(()=>{
    使用(var c=dbProviderFactorys.GetFactory(“”.CreateConnection())
    {
    c、 ConnectionString=“”;
    c、 Open();
    }
    })).Should().Throw().WithMessage(“无法找到请求的.Net Framework数据提供程序。可能未安装它”);
    }
    
    谢谢-我更新了这个问题,因为我认为“非类型化”是一个错误的术语。这是一个非常优雅和成熟的解决方案。我可能会称它为“InvokeAction”,因此这个名字暗示了我们实际调用的是什么(而不是泛型委托),但它确实对我有用:)我不同意它“很少有用而且……”。在使用lambda调用Begin/Invoke的情况下,您当然不关心委托类型是否是自动生成的,我们只希望调用。在什么情况下,接受委托(基类型)的方法会关心具体类型是什么?另外,extensi的目的是什么
    [Fact]
    public void Pass_Open_Connection_Without_Provider()
    {
        ((Action)(() => {
            using (var c = DbProviderFactories.GetFactory("<provider>").CreateConnection())
            {
                c.ConnectionString = "<connection>";
                c.Open();
            }
        })).Should().Throw<Exception>().WithMessage("Unable to find the requested .Net Framework Data Provider.  It may not be installed.");
    }