I’m a bit weak on how some delegates behave, such as passing a method as the parameter to be invoked. While trying to do some NUnit test scripts, I have something that I need to run many test with. Each of these tests requires a GUI created and thus the need for an STA thread. So, I have something like
public class MyTest
{
// the Delegate "ThreadStart" is part of the System.Threading namespace and is defined as
// public delegate void ThreadStart();
protected void Start_STA_Thread(ThreadStart whichMethod)
{
Thread thread = new Thread(whichMethod);
thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
thread.Start();
thread.Join();
}
[Test]
public void Test101()
{
// Since the thread issues an INVOKE of a method, I'm having it call the
// corresponding "FromSTAThread" method, such as
Start_STA_Thread( Test101FromSTAThread );
}
protected void Test101FromSTAThread()
{
MySTA_RequiredClass oTmp = new MySTA_RequiredClass();
Assert.IsTrue( oTmp.DoSomething() );
}
}
This part all works fine… Now the next step. I now have a different set of tests that ALSO require an STA thread. However, each “thing” I need to do requires two parameters… both strings (for this case).
How do I go about declaring proper delegate so I can pass in the method I need to invoke, AND the two string parameters in one shot… I may have 20+ tests to run with in this pattern and may have future of other similar tests with different parameter counts and types of parameters too.
Thanks.
You can use a lambda expression.
Here is a modified method you want to call in your test accepting parameters:
And here is how to call
Start_STA_Threadusing a lambda expression:The lambda expression in this case is:
This matches the required
ThreadStartdelegate. Both are methods without parameters having a void return type.If you are unfamiliar with lambda expression you can learn more by reading Lambda Expressions (C# Programming Guide) on MSDN. A lambda expression is an anonymous function and here it creates a delegate. The delegate is passed to the thread and when the thread starts the anonymous function is executed. In this case, because a thread is involved, the anonymous function is executed on the thread you create and not the test runner thread.
When you use a lambda expression in C# the compiler will emit a function with a “funny” name representing the anonymous function. If the lambda expression uses variables from the lexical environment (e.g. a local variable defined before the lambda expression) the compiler will generate the anonymous function inside a special type (again with a “funny” name) to create what is called a closure. This allows the compiler to capture the variables from the lexical environment into the state of the special type and use it when the the anonymous function later is executed. In your case this happens when the thread starts.
The code above will generate something like this (the “funny” name
<Test101>b__0will of course not compile but it is perfectly valid in IL):Note how
<Test101>b__0can be used as aThreadStartdelegate.