일반 위임 매개 변수로 제공 될 때 람다 식을 캐스트해야하는 이유
System.Windows.Forms.Control.Invoke (Delegate 메소드) 메소드를 사용하십시오.
왜 컴파일 시간 오류가 발생합니까?
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));
방법이 평범한 위임을 기대할 때?
람다 식은 대리자 형식 또는 식 트리로 변환 할 수 있지만 어떤 대리자 형식 을 알아야 합니다. 서명 만 아는 것만으로는 충분하지 않습니다. 예를 들어 내가 가지고 있다고 가정하십시오.
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, 응답 대기) SomeMethod를 차단해야하는 경우 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
}
And a final tip because things can get tangled: WaitHandles stall the thread. This is what they're supposed to do. If you try to marshal onto the UI thread while you have it stalled, your app will hang. In this case (a) some serious refactoring is in order, and (b) as a temporary hack you can wait like this:
bool wait = true;
ThreadPool.QueueUserWorkItem ((state) =>
{
//do asynch stuff
wait = false;
});
while (wait) Thread.Sleep(100);
Peter Wone. you are da man. Taking your concept a bit further, I came up with these two functions.
private void UIA(Action action) {this.Invoke(action);}
private T UIF<T>(Func<T> func) {return (T)this.Invoke(func);}
I place these two functions into my Form app, and I can make calls from background workers like this
int row = 5;
string ip = UIF<string>(() => this.GetIp(row));
bool r = GoPingIt(ip);
UIA(() => this.SetPing(i, r));
Maybe a bit lazy, but i don't have to setup worker done functions, which comes in super handy in cases such as this
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));
}
Essentially, get some ip addresses from a gui DataGridView, ping them, set the resulting icons to green or red, and reenable buttons on the form. Yes, it is a "parallel.for" in a backgroundworker. Yes it is a LOT of invoking overhead, but its negligible for short lists, and much more compact code.
I tried to build this upon @Andrey Naumov's answer. May be this is a slight improvement.
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;
}
}
Where type parameter S is the formal parameter (the input parameter, which is minimum required to infer rest of the types). Now you can call it like:
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!");
You can have additional overloads for Action<S> and Expression<Action<S>> similarly in the same class. For other built in delegate and expression types, you will have to write separate classes like Lambda, Lambda<S, T>, Lambda<S, T, U> etc.
Advantage of this I see over the original approach:
One less type specification (only the formal parameter needs to be specified).
Which gives you the freedom to use it against any
Func<int, T>, not just whenTis say,string, as shown in examples.Supports expressions straight away. In the earlier approach you will have to specify types again, like:
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!");for expressions.
Extending the class for other delegate (and expression) types is similarly cumbersome like above.
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());
In my approach you have to declare types only once (that too one less for Funcs).
One another way to implement Andrey's answer is like not going fully generic
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;
}
So things reduce to:
var l = Lambda<int>.Expression;
var e1 = l(x => x.ToString());
var e2 = l(x => "Hello!");
var e3 = l(x => x + x);
That's even less typing, but you lose certain type safety, and imo, this is not worth it.
Bit late to the party but you can also cast like this
this.BeginInvoke((Action)delegate {
// do awesome stuff
});
this.Dispatcher.Invoke((Action)(() => { textBox1.Text = "Test 123"; }));
Playing with XUnit and Fluent Assertions it was possible to use this inline capability in a way I find really cool.
Before
[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");
}
After
[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.");
}
'programing tip' 카테고리의 다른 글
| 초기 커밋을 참조하는 방법? (0) | 2020.07.14 |
|---|---|
| numpy를 가져온 후 멀티 프로세싱에서 단일 코어 만 사용하는 이유는 무엇입니까? (0) | 2020.07.14 |
| Visual Studio 2010없이 MSBuild 4.0 설치 (0) | 2020.07.14 |
| JAR 파일 외부의 특성 파일 읽기 (0) | 2020.07.14 |
| Java 용 BDD 프레임 워크의 차이점은 무엇입니까? (0) | 2020.07.14 |