I have the following code:
private void StartTask<T>(string parameter)
{
dynamic instance = (T) Activator.CreateInstance(typeof(T), parameter);
Task.Factory.StartNew(() => instance.DoCompare());
}
This does not work and the "DoCompare()" method does not get called...How do I call a method in a class with parameters in a generic type method?
Class I am initiating:
public class Something {
private string _parameter;
public Something(string parameter) {
_parameter = parameter;
}
public void DoCompare(){
//Do longrunning task
}
}
EDIT: Removed constraint BaseClass5 because of confusion
EDIT2: I get: A first chance exception of type
'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException'
EDIT3:
This seems to work, the issue seems to be Task.Factory.StartNew:
private void StartTaskAwesomium<T>() where T
{
dynamic instance = (T) Activator.CreateInstance(typeof (T), parameter);
instance.Test();
}
In theory your code should work, but it is a "fire-and-forget" pattern, meaning it will be executed sometime, but you have no control over that or can check if it actually was executed or if there was an exception.
Maybe change it to:
private async void StartTask<T>(string parameter) where T : BaseClass5
{
BaseClass5 instance = (T)Activator.CreateInstance(typeof(T), parameter);
await Task.Factory.StartNew(() => instance.DoCompare());
}
No need for the dynamic as because of your generic constraint you know that T must be BaseClass5 or based on it.
Or return the task to await (or task.Wait()) it elsewhere:
private Task StartTask<T>(string parameter)
{
T instance = (T)Activator.CreateInstance(typeof(T), parameter);
return Task.Factory.StartNew(() => instance.DoCompare());
}
The solution was to wrap the creation of the class in the Task itself like this:
private void StartTask<T>(string connectionId) where T : BaseClass5
{
Task.Factory.StartNew(() =>
{
dynamic instance = (T) Activator.CreateInstance(typeof (T), connectionId);
instance.DoCompare();
});
}
private void StartTask<T>(string parameter)
{
dynamic instance = (T) Activator.CreateInstance(typeof(T), parameter);
Task.Factory.StartNew(() => {instance.DoCompare();});
}
This seems to work.
Related
I would like to cast a Task<object> to Task<?> at the runtime like that :
return task.CastTo(targetType);
Here is my best approach:
[TestMethod]
public void CastToString()
{
var task = CallTaskString("test");
var casting = CastTo<string>(task);
Assert.IsNotNull(casting);
var targetTask = casting as Task<string>;
Assert.IsNotNull(targetTask);
}
private async Task<T> CastTo<T>(Task task)
{
await task.ConfigureAwait(false);
return (T)((dynamic)task).Result;
}
public Task<object> CallTaskString(string value)
{
return Task.Run<object>(() => value);
}
I just want to replace the T by an argument Type targetType like that:
public static async Task CastTo(this Task task, Type targetType).
Thanks
It's possible to call a generic method at runtime. Maybe this can help you :
public static class TaskExtension
{
public static Task CastTo(this Task task, Type targetType)
{
var taskType = task.GetType();
var srcType = taskType.GetGenericArguments().First();
var method = typeof(TaskExtension).GetMethods(BindingFlags.NonPublic | BindingFlags.Static).Single(m => m.Name == nameof(CastToImpl)).MakeGenericMethod(srcType, targetType);
return (Task)method.Invoke(null, new[] { task });
}
private static Task<R> CastToImpl<T, R>(Task<T> t)
where R : T
{
return t.ContinueWith(t => (R)t.Result);
}
}
You can use like :
[TestMethod]
public void CastToString1()
{
var task = CallTaskString("test");
var casting = task.CastTo(typeof(string));
Assert.IsNotNull(casting);
var targetTask = casting as Task<string>;
Assert.IsNotNull(targetTask);
}
I would like to cast a Task to Task<?> at the runtime
That isn't possible if the T in Task<T> is anything other than object, because Task<T> is invariant.
You seem to be overthinking this; there's no need to invent a generic way of converting the Task, you can simply cast the Result of the Task<object>:
public async Task<string> SomeInterfaceMethodAsync()
{
return (string) await SomeMethodThatReturnsTaskObjectAsync();
}
Here is my test code: the extension method GetInstructions is from here: https://gist.github.com/jbevain/104001
using System;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
typeof(TestClass)
.GetMethods()
.Where(method => method.Name == "Say" || method.Name == "Hello")
.ToList()
.ForEach(method =>
{
var calls = method.GetInstructions()
.Select(x => x.Operand as MethodInfo)
.Where(x => x != null)
.ToList();
Console.WriteLine(method);
calls.ForEach(call =>
{
Console.WriteLine($"\t{call}");
call.GetGenericArguments().ToList().ForEach(arg => Console.WriteLine($"\t\t{arg.FullName}"));
});
});
Console.ReadLine();
}
}
class TestClass
{
public async Task Say()
{
await HelloWorld.Say<IFoo>();
HelloWorld.Hello<IBar>();
}
public void Hello()
{
HelloWorld.Say<IFoo>().RunSynchronously();
HelloWorld.Hello<IBar>();
}
}
class HelloWorld
{
public static async Task Say<T>() where T : IBase
{
await Task.Run(() => Console.WriteLine($"Hello from {typeof(T)}.")).ConfigureAwait(false);
}
public static void Hello<T>() where T : IBase
{
Console.WriteLine($"Hello from {typeof(T)}.");
}
}
interface IBase
{
Task Hello();
}
interface IFoo : IBase
{
}
interface IBar : IBase
{
}
}
Here is run result as the screenshot shown:
System.Threading.Tasks.Task Say()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder Create()
Void Start[<Say>d__0](<Say>d__0 ByRef)
ConsoleApp1.TestClass+<Say>d__0
System.Threading.Tasks.Task get_Task()
Void Hello()
System.Threading.Tasks.Task Say[IFoo]()
ConsoleApp1.IFoo
Void RunSynchronously()
Void Hello[IBar]()
ConsoleApp1.IBar
NON-ASYNC calls got correct generic parameters, but ASYNC calls cannot.
My question is: where are the generic parameters stored for ASYNC calls?
Thanks a lot.
The async methods are not that easy.
The C# compiler will generate a comprehensive state machine out of an async method. So the body of the TestClass.Say method will be completely overwritten by the compiler. You can read this great blog post if you want to dive deeper into the async state machinery.
Back to your question.
The compiler will replace the method body with something like this:
<Say>d__0 stateMachine = new <Say>d__0();
stateMachine.<>4__this = this;
stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
stateMachine.<>1__state = -1;
AsyncTaskMethodBuilder <>t__builder = stateMachine.<>t__builder;
<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
<Say>d__0 in this code is a compiler-generated type. It has special characters it its name to prevent you from being able to use this type in your code.
<Say>d__0 is an IAsyncStateMachine implementation. The main logic is contained in its MoveNext method.
It will look similar to this:
TaskAwaiter awaiter;
if (state != 0)
{
awaiter = HelloWorld.Say<IFoo>().GetAwaiter();
if (!awaiter.IsCompleted)
{
// ...
builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
return;
}
}
else
{
awaiter = this.awaiter;
state = -1;
}
awaiter.GetResult();
HelloWorld.Hello<IBar>();
Note that your HelloWorld.Say<IFoo>() call is now here, in this method, not in your original TestClass.Say.
So, to get the generic type information from your method, you will need to inspect the MoveNext state machine method instead of the original TestClass.Say. Search for the call instructions there.
Something like this:
Type asyncStateMachine =
typeof(TestClass)
.GetNestedTypes(BindingFlags.NonPublic)
.FirstOrDefault(
t => t.GetCustomAttribute<CompilerGeneratedAttribute>() != null
&& typeof(IAsyncStateMachine).IsAssignableFrom(t));
MethodInfo method = asyncStateMachine.GetMethod(
nameof(IAsyncStateMachine.MoveNext),
BindingFlags.NonPublic | BindingFlags.Instance);
List<MethodInfo> calls = method.GetInstructions()
.Select(x => x.Operand as MethodInfo)
.Where(x => x != null)
.ToList();
// etc
Output:
Void MoveNext()
System.Threading.Tasks.Task Say[IFoo]()
ConsoleApp1.IFoo
System.Runtime.CompilerServices.TaskAwaiter GetAwaiter()
Boolean get_IsCompleted()
Void AwaitUnsafeOnCompleted[TaskAwaiter,<Say>d__0](System.Runtime.CompilerServices.TaskAwaiter ByRef, <Say>d__0 ByRef)
System.Runtime.CompilerServices.TaskAwaiter
ConsoleApp1.TestClass+<Say>d__0
Void GetResult()
Void Hello[IBar]()
ConsoleApp1.IBar
Void SetException(System.Exception)
Void SetResult()
Note that this code depends on current IAsyncStatMachine implementation internals. If the C# compiler changes that internal implementation, this code might break.
You can try getting the generic method info and that way you can find the IFoo generic type argument from this (code taken from the msdn):
private static void DisplayGenericMethodInfo(MethodInfo mi)
{
Console.WriteLine("\r\n{0}", mi);
Console.WriteLine("\tIs this a generic method definition? {0}",
mi.IsGenericMethodDefinition);
Console.WriteLine("\tIs it a generic method? {0}",
mi.IsGenericMethod);
Console.WriteLine("\tDoes it have unassigned generic parameters? {0}",
mi.ContainsGenericParameters);
// If this is a generic method, display its type arguments.
//
if (mi.IsGenericMethod)
{
Type[] typeArguments = mi.GetGenericArguments();
Console.WriteLine("\tList type arguments ({0}):",
typeArguments.Length);
foreach (Type tParam in typeArguments)
{
// IsGenericParameter is true only for generic type
// parameters.
//
if (tParam.IsGenericParameter)
{
Console.WriteLine("\t\t{0} parameter position {1}" +
"\n\t\t declaring method: {2}",
tParam,
tParam.GenericParameterPosition,
tParam.DeclaringMethod);
}
else
{
Console.WriteLine("\t\t{0}", tParam);
}
}
}
}
I'd like to assign a function to the OnTicketReceived property of the OAuthEvents object (https://learn.microsoft.com/en-us/aspnet/core/api/microsoft.aspnetcore.authentication.oauth.oauthevents)
It is a Func<TicketReceivedContext, Task>
public class MyClass
{
public void Config()
{
var o = new OAuthEvents
{
OnTicketReceived = MyFunc; //Doesn't work
};
}
public Task MyFunc(TicketReceivedContext ctx)
{
return Task.CompletedTask;
}
}
I know I can write an anonymous method using a lambda but I don't want to do that because I wish to reuse the method in multiple places. How is this supposed to be done?
How can I make the Predicate return a bool value from async method C#
private void OnFilterTextBoxTextChangedHandler(object oSender, TextChangedEventArgs oArgs)
{
//Other operations
_oCollectionView.Filter = new Predicate<object>(DoFilter); //wrong return type
}
Returning method
private async Task<bool> DoFilter(object oObject)
{
if (_sFilterText == "")
{
return true;
}
return false;
}
Predicate<T> is a delegate. You don't need to instantiate a new predicate when adding a filter to your CollectionView. Instead, you can add your filter like this:
_oCollectionView.Filter = DoFilter;
Second, the signature of the CollectionView.Filter delegate is public delegate bool Predicate<object>(object obj). The parameter obj is the element of the CollectionView that is being evaluated. You cannot alter this signature to make it asynchronous.
In your example, I would look at doing the following:
constructor()
{
InitializeComponent();
// Alternatively put this in an initialization method.
_oCollectionView.Filter = DoFilter;
}
private async void OnFilterTextBoxTextChangedHandler(object oSender, TextChangedEventArgs oArgs)
{
// Other operations
// Asynchronous processing
await SetupFilterAsync();
_oCollectionView.Refresh();
}
private async Task SetupFilterAsync()
{
// Do whatever you need to do async.
}
private bool DoFilter(object obj)
{
// Cast object to the type your CollectionView is holding
var myObj = (MyType) obj;
// Determine whether that element should be filtered
return myObj.ShouldBeFiltered;
}
You can also define your filter as a lambda, which would look like this and eliminate the DoFilter method:
_oCollectionView.Filter = x => ((MyType)x).ShouldBeFiltered;
I found this post that explains how to pass methods as parameters in C#.
What I need to know is how to return a method as the result of another method invocation.
method = DoSomething()
result = method()
you need to use either Action<T> or Func<T>
Like this:
private Action<string> Returns(string user)
{
return () =>
{
Console.WriteLine("Hey {0}", user);
};
}
or this:
private Func<bool> TestsIsThirty(int value)
{
return () => value == 30;
}
Most probably you want your return type to be a Delegate.
Check out the Action and Func delegates.
var method =()=> DoSomething();
result = method();