일반 대의원 매개 변수로 제공 될 때 람다 표현식을 캐스트 해야하는 이유

StackOverflow https://stackoverflow.com/questions/411579

  •  03-07-2019
  •  | 
  •  

문제

메소드 System.windows.forms.control.invoke (Delegate Method)를 취하십시오.

왜 이것이 컴파일 시간 오류를 제공합니까?

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 되려고? 예, 컴파일러입니다 ~할 수 있었다 적절한 서명으로 새 대표 유형을 생성하지만 거의 유용하지 않으며 오류 확인 기회가 줄어 듭니다.

쉽게 전화하기를 원한다면 Control.Invoke 와 함께 Action 가장 쉬운 방법은 제어 할 확장 방법을 추가하는 것입니다.

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());
    }
}

사람들이 UI 스레드에 마샬링하려고하기 때문에 사람들이 이것을 얻는 시간의 9 분의 1입니다. 게으른 방법은 다음과 같습니다.

static void UI(Action action) 
{ 
  System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(action); 
}

이제 입력되었으므로 문제가 사라지고 (QV Skeet의 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, 응답 대기) Waithandle (QV MSDN Waitall, Waitany, Waitone).

Autoresetevent는 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);

피터 윈. 당신은 Da Man입니다. 당신의 개념을 조금 더 가져 가면서, 나는이 두 기능을 생각해 냈습니다.

private void UIA(Action action) {this.Invoke(action);}
private T UIF<T>(Func<T> func) {return (T)this.Invoke(func);}

이 두 기능을 내 양식 앱에 배치하고 이와 같은 백그라운드 작업자로부터 전화를 걸 수 있습니다.

int row = 5;
string ip = UIF<string>(() => this.GetIp(row));
bool r = GoPingIt(ip);
UIA(() => this.SetPing(i, r));

어쩌면 약간 게으르지 만 작업자가 기능을 설정할 필요는 없습니다.

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));
}

기본적으로 GUI DatagridView에서 일부 IP 주소를 가져와 핑하고 결과 아이콘을 녹색 또는 빨간색으로 설정하고 양식에 다시 활성화 가능한 버튼을 설정하십시오. 예, 배경 작업자의 "평행"입니다. 예, 오버 헤드를 호출하는 것은 많지만 짧은 목록과 훨씬 더 컴팩트 한 코드는 무시할 수 있습니다.

나는 이것을 만들려고 노력했다 @andrey naumov대답. 이것이 약간 개선 될 수 있습니다.

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;
    }
}

여기서 유형 매개 변수 S 공식 매개 변수입니다 (입력 매개 변수는 나머지 유형을 추론하는 데 필요한 최소입니다). 이제 당신은 그것을 다음과 같이 부를 수 있습니다.

var l = new Lambda<int>();
var d1 = l.Func(x => x.ToString());
var e1 = l.Expression(x => "Hello!");
var d2 = l.Func(x => x + x);

//or if you have only one lambda, consider a static overload
var e2 = Lambda<int>.CreateExpression(x => "Hello!");

추가 오버로드가있을 수 있습니다 Action<S> 그리고 Expression<Action<S>> 같은 클래스에서도 마찬가지로. 을 위한 다른 대의원 및 표현 유형으로 내장되면 다음과 같이 별도의 클래스를 작성해야합니다. Lambda, Lambda<S, T>, Lambda<S, T, U> 등.

이것의 장점은 원래 접근 방식에 비해 볼 수 있습니다.

  1. 하나의 유형 사양이 적습니다 (공식 매개 변수 만 지정해야 함).

  2. 당신에게 당신에게 그것을 자유롭게 사용할 수있는 Func<int, T>, 언제뿐만 아니라 T 말한다, string, 예에서 볼 수 있듯이.

  3. 표현을 곧바로 지원합니다. 이전 접근 방식에서는 다음과 같은 유형을 다시 지정해야합니다.

    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!");
    

    표현을 위해.

  4. 다른 대의원 (및 표현) 유형에 대한 클래스를 확장하는 것은 위와 같이 유사하게 번거 롭습니다.

    var e = Lambda<Action<int>>.Cast(x => x.ToString());
    
    //or for Expression<Action<T>> if 'Cast' is an instance member on non-generic 'Lambda' class:
    var e = lambda.Cast<Expression<Action<int>>>(x => x.ToString());
    

내 접근 방식에서는 한 번만 유형을 선언해야합니다 (너무 적은 Func에스).


Andrey의 답변을 구현하는 또 다른 방법은 완전히 일반적인 것으로 가지 않는 것과 같습니다.

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

그래서 상황이 줄어 듭니다.

var l = Lambda<int>.Expression;
var e1 = l(x => x.ToString());
var e2 = l(x => "Hello!");
var e3 = l(x => x + x);

타이핑은 훨씬 적지 만 특정 유형 안전을 잃고 이모, 이것은 그만한 가치가 없습니다.

파티에 늦었지만 이렇게 캐스팅 할 수도 있습니다.

this.BeginInvoke((Action)delegate {
    // do awesome stuff
});
 this.Dispatcher.Invoke((Action)(() => { textBox1.Text = "Test 123"; }));
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top