The NancyFx (2.x) NancyModule.Get() method is defined as:
public virtual void Get(string path, Func<dynamic, object> action, [Func<NancyContext, bool> condition = null], [string name = null]);
The normal usage is:
public class MyModule
{
public MyModule()
{
this.Get("/", parameters => {
this.RequestHandler = new RequestHandler();
return this.RequestHandler.HandleRequest("/", parameters, someOtherInfo);
});
}
}
I want to define the second parameter as a property, so that I can use for several paths like this:
public class MyModule
{
Func<dynamic, object> indexHandler = parameters => {
// Error: Keyword "this" is not available in this context
this.RequestHandler = new RequestHandler();
// Error: Keyword "this" is not available in this context
return this.RequestHandler.HandleRequest("/", parameters, someOtherInfo);
};
public MyModule()
{
this.Get("/", indexHandler);
this.Get("/index", indexHandler);
}
}
If I do this it works:
public class MyModule
{
public MyModule()
{
Func<dynamic, object> indexHandler = parameters => {
this.RequestHandler = new RequestHandler();
return this.RequestHandler.HandleRequest("/", parameters, someOtherInfo);
};
this.Get("/", indexHandler);
this.Get("/index", indexHandler);
}
}
But I don't want to define it in the constructor. What am I doing wrong? Is there any other way to do this?
MVCE
Dependancy Package: Nancy (Version: 2.0.0-clinteastwood)
using Nancy;
using Nancy.Responses.Negotiation;
namespace MyNamespace
{
public class MyModule : NancyModule
{
private RequestHandler RequestHandler;
private object IndexHandler(dynamic parameters)
{
this.RequestHandler = new RequestHandler();
var someOtherInfo = "";
return this.RequestHandler.HandleRequest("/", parameters, someOtherInfo);
}
public MyModule()
{
this.Get("/", IndexHandler);
this.Get("/index", IndexHandler);
this.Get("/home", parameters => {
return this.Negotiate.WithView("myview.html");
});
}
}
public class RequestHandler
{
public Negotiator HandleRequest(string path, dynamic parameters, string someOtherInfo)
{
return new Negotiator(new NancyContext());
}
}
}
This should work:
public class MyModule
{
public MyModule()
{
this.Get("/", IndexHandler);
this.Get("/index", IndexHandler);
}
private object IndexHandler(dynamic parameters) {
this.RequestHandler = new RequestHandler();
return this.RequestHandler.HandleRequest("/", parameters, someOtherInfo);
}
}
Andrew's answer is valid and should've been sufficient but apparently (in your MVCE) that method definition doesn't exist.
Here's the correct definition (at least the one VS wants):
public virtual void Get(string path, Func<object, Task<object>> action, Func<NancyContext, bool> condition = null, string name = null)
Luckly your HandleRequest is awaitable so you only need to edit the return type.
Here's the correct definition:
private Task<object> IndexHandler(dynamic parameters)
{
this.RequestHandler = new RequestHandler();
var someOtherInfo = "";
return this.RequestHandler.HandleRequest("/", parameters, someOtherInfo);
}
Hope this helps!
Related
In xUnit I can have a Theory test that uses generics in this form:
[Theory]
[MemberData(SomeScenario)]
public void TestMethod<T>(T myType)
{
Assert.Equal(typeof(double), typeof(T));
}
public static IEnumerable<object[]> SomeScenario()
{
yield return new object[] { 1.23D };
}
Which will give me the generic T parameter as double. Is it possible to use MemberData to specify the generic type parameter for a test with a signature like:
[Theory]
[MemberData(SomeTypeScenario)]
public void TestMethod<T>()
{
Assert.Equal(typeof(double), typeof(T));
}
If it is not possible with MemberData or any other provided attribute (which I'm suspecting that it isn't), is it possible to create an attribute for Xunit that can achieve this? Maybe something along the lines of specifying Types in the Scenarios method and using reflection in a similar manner to Jon Skeet's answer here: Generics in C#, using type of a variable as parameter
You can simply include Type as an input parameter instead. E.g.:
[Theory]
[MemberData(SomeTypeScenario)]
public void TestMethod(Type type) {
Assert.Equal(typeof(double), type);
}
public static IEnumerable<object[]> SomeScenario() {
yield return new object[] { typeof(double) };
}
There is no need to go with generics on xunit.
Edit (if you really need generics)
1) You need to subclass ITestMethod to persist generic method info, it also has to implement IXunitSerializable
// assuming namespace Contosco
public class GenericTestMethod : MarshalByRefObject, ITestMethod, IXunitSerializable
{
public IMethodInfo Method { get; set; }
public ITestClass TestClass { get; set; }
public ITypeInfo GenericArgument { get; set; }
/// <summary />
[Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")]
public GenericTestMethod()
{
}
public GenericTestMethod(ITestClass #class, IMethodInfo method, ITypeInfo genericArgument)
{
this.Method = method;
this.TestClass = #class;
this.GenericArgument = genericArgument;
}
public void Serialize(IXunitSerializationInfo info)
{
info.AddValue("MethodName", (object) this.Method.Name, (Type) null);
info.AddValue("TestClass", (object) this.TestClass, (Type) null);
info.AddValue("GenericArgumentAssemblyName", GenericArgument.Assembly.Name);
info.AddValue("GenericArgumentTypeName", GenericArgument.Name);
}
public static Type GetType(string assemblyName, string typeName)
{
#if XUNIT_FRAMEWORK // This behavior is only for v2, and only done on the remote app domain side
if (assemblyName.EndsWith(ExecutionHelper.SubstitutionToken, StringComparison.OrdinalIgnoreCase))
assemblyName = assemblyName.Substring(0, assemblyName.Length - ExecutionHelper.SubstitutionToken.Length + 1) + ExecutionHelper.PlatformSuffix;
#endif
#if NET35 || NET452
// Support both long name ("assembly, version=x.x.x.x, etc.") and short name ("assembly")
var assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == assemblyName || a.GetName().Name == assemblyName);
if (assembly == null)
{
try
{
assembly = Assembly.Load(assemblyName);
}
catch { }
}
#else
System.Reflection.Assembly assembly = null;
try
{
// Make sure we only use the short form
var an = new AssemblyName(assemblyName);
assembly = System.Reflection.Assembly.Load(new AssemblyName { Name = an.Name, Version = an.Version });
}
catch { }
#endif
if (assembly == null)
return null;
return assembly.GetType(typeName);
}
public void Deserialize(IXunitSerializationInfo info)
{
this.TestClass = info.GetValue<ITestClass>("TestClass");
string assemblyName = info.GetValue<string>("GenericArgumentAssemblyName");
string typeName = info.GetValue<string>("GenericArgumentTypeName");
this.GenericArgument = Reflector.Wrap(GetType(assemblyName, typeName));
this.Method = this.TestClass.Class.GetMethod(info.GetValue<string>("MethodName"), true).MakeGenericMethod(GenericArgument);
}
}
2) You need to write your own discoverer for generic methods, it has to be subclass of IXunitTestCaseDiscoverer
// assuming namespace Contosco
public class GenericMethodDiscoverer : IXunitTestCaseDiscoverer
{
public GenericMethodDiscoverer(IMessageSink diagnosticMessageSink)
{
DiagnosticMessageSink = diagnosticMessageSink;
}
protected IMessageSink DiagnosticMessageSink { get; }
public IEnumerable<IXunitTestCase> Discover(ITestFrameworkDiscoveryOptions discoveryOptions,
ITestMethod testMethod, IAttributeInfo factAttribute)
{
var result = new List<IXunitTestCase>();
var types = factAttribute.GetNamedArgument<Type[]>("Types");
foreach (var type in types)
{
var typeInfo = new ReflectionTypeInfo(type);
var genericMethodInfo = testMethod.Method.MakeGenericMethod(typeInfo);
var genericTestMethod = new GenericTestMethod(testMethod.TestClass, genericMethodInfo, typeInfo);
result.Add(
new XunitTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(),
genericTestMethod));
}
return result;
}
}
3) Finally you can make your attribute for generic methods and hook it to your custom discoverer by XunitTestCaseDiscoverer attribute
// assuming namespace Contosco
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
[XunitTestCaseDiscoverer("Contosco.GenericMethodDiscoverer", "Contosco")]
public sealed class GenericMethodAttribute : FactAttribute
{
public Type[] Types { get; private set; }
public GenericMethodAttribute(Type[] types)
{
Types = types;
}
}
Usage:
[GenericMethod(new Type[] { typeof(double), typeof(int) })]
public void TestGeneric<T>()
{
Assert.Equal(typeof(T), typeof(double));
}
If I use a a factory interface such as this
public interface IBoredFactory
{
IBored Create(<some args>)
}
and in the binding do this
kernel.Bind<IBoredFactory>().ToFactory();
kernel.Bind<IBored>().To<Yawn>();
it works just as expected, and the code returns a Yawn
But if I change the Factory interface to this
public interface IBoredFactory
{
Task<IBored> Create(<some args>)
}
The factory proxy gets built, but the thread never returns from the Create(), the caller endlessly awaits.
Am I missing something here ?
As #Andreas Appelros suggests Ninject is returning an unfinished Task<T>.
As there's actually no async ninject API it does not really make sense to use async-await for resolving instances, even if there'd be something async involved in the creation, this would first need to be converted to a synchronous-call to satisfy ninject's binding API's ("sync over async") - which is not really a good thing to do. Then do convert it it to async again ("async over sync") doesn't make any more sense, either, as it's just adding overhead.
However, if for some reason you still want to do it, you'll have to use a custom IInstanceProvider, for example the following will work (code can be improved, though):
public class AsyncInstanceProvider : StandardInstanceProvider
{
public override object GetInstance(
IInstanceResolver instanceResolver, MethodInfo methodInfo, object[] arguments)
{
object result = base.GetInstance(
instanceResolver, methodInfo, arguments);
if (ReturnsTaskResult(methodInfo))
{
return CreateFinishedTask(methodInfo, result);
}
return result;
}
protected override Type GetType(MethodInfo methodInfo, object[] arguments)
{
if (ReturnsTaskResult(methodInfo))
{
return methodInfo.ReturnType.GenericTypeArguments[0];
}
return base.GetType(methodInfo, arguments);
}
private bool ReturnsTaskResult(MethodInfo methodInfo)
{
return (methodInfo.ReturnType.IsGenericType
&& methodInfo.ReturnType.GetGenericTypeDefinition() == typeof(Task<>));
}
private object CreateFinishedTask(MethodInfo methodInfo, object result)
{
var openGenericMethod = typeof(Task).GetMethod("FromResult");
var closedGenericMethod = openGenericMethod.MakeGenericMethod(
methodInfo.ReturnType.GenericTypeArguments[0]);
return closedGenericMethod.Invoke(null, new object[1] { result });
}
}
Globally Applying the new IInstanceProvider
If you want to use that Instance Provider globally, perform the following rebind after loading the Factory extension:
Rebind<IInstanceProvider>().To<AsyncInstanceProvider>();
Complete Test-Code
And, for reference, the complete code including a sync and an async test (requires xunit and FluentAssertion nuget packages):
namespace NinjectTest.AsyncFactory_44983826
{
using System;
using System.Reflection;
using System.Threading.Tasks;
using Ninject;
using Ninject.Extensions.Factory;
using Ninject.Extensions.Factory.Factory;
using FluentAssertions;
using Xunit;
public class AsyncInstanceProvider : StandardInstanceProvider
{
public override object GetInstance(IInstanceResolver instanceResolver, MethodInfo methodInfo, object[] arguments)
{
object result = base.GetInstance(instanceResolver, methodInfo, arguments);
if (ReturnsTaskResult(methodInfo))
{
return CreateFinishedTask(methodInfo, result);
}
return result;
}
protected override Type GetType(MethodInfo methodInfo, object[] arguments)
{
if (ReturnsTaskResult(methodInfo))
{
return methodInfo.ReturnType.GenericTypeArguments[0];
}
return base.GetType(methodInfo, arguments);
}
private bool ReturnsTaskResult(MethodInfo methodInfo)
{
return (methodInfo.ReturnType.IsGenericType && methodInfo.ReturnType.GetGenericTypeDefinition() == typeof(Task<>));
}
private object CreateFinishedTask(MethodInfo methodInfo, object result)
{
var openGenericMethod = typeof(Task).GetMethod("FromResult");
var closedGenericMethod = openGenericMethod.MakeGenericMethod(methodInfo.ReturnType.GenericTypeArguments[0]);
return closedGenericMethod.Invoke(null, new object[1] { result });
}
}
public class Foo
{
public string Id { get; private set; }
public Bar Bar { get; private set; }
public Foo(string id, Bar bar)
{
this.Id = id;
this.Bar = bar;
}
}
public class Bar { }
public interface IFactory
{
Foo Create(string id);
Task<Foo> CreateAsync(string id);
}
public class Test
{
[Fact]
public void SyncTest()
{
var kernel = new StandardKernel();
kernel
.Bind<IFactory>()
.ToFactory(() => new AsyncInstanceProvider());
const string expectedId = "Hello You!";
kernel.Get<IFactory>().Create(expectedId)
.Id.Should().Be(expectedId);
}
[Fact]
public async Task AsyncTest()
{
var kernel = new StandardKernel();
kernel
.Bind<IFactory>()
.ToFactory(() => new AsyncInstanceProvider());
const string expectedId = "Hello You!";
Foo result = await kernel.Get<IFactory>().CreateAsync(expectedId);
result.Id.Should().Be(expectedId);
}
}
}
In xUnit I can have a Theory test that uses generics in this form:
[Theory]
[MemberData(SomeScenario)]
public void TestMethod<T>(T myType)
{
Assert.Equal(typeof(double), typeof(T));
}
public static IEnumerable<object[]> SomeScenario()
{
yield return new object[] { 1.23D };
}
Which will give me the generic T parameter as double. Is it possible to use MemberData to specify the generic type parameter for a test with a signature like:
[Theory]
[MemberData(SomeTypeScenario)]
public void TestMethod<T>()
{
Assert.Equal(typeof(double), typeof(T));
}
If it is not possible with MemberData or any other provided attribute (which I'm suspecting that it isn't), is it possible to create an attribute for Xunit that can achieve this? Maybe something along the lines of specifying Types in the Scenarios method and using reflection in a similar manner to Jon Skeet's answer here: Generics in C#, using type of a variable as parameter
You can simply include Type as an input parameter instead. E.g.:
[Theory]
[MemberData(SomeTypeScenario)]
public void TestMethod(Type type) {
Assert.Equal(typeof(double), type);
}
public static IEnumerable<object[]> SomeScenario() {
yield return new object[] { typeof(double) };
}
There is no need to go with generics on xunit.
Edit (if you really need generics)
1) You need to subclass ITestMethod to persist generic method info, it also has to implement IXunitSerializable
// assuming namespace Contosco
public class GenericTestMethod : MarshalByRefObject, ITestMethod, IXunitSerializable
{
public IMethodInfo Method { get; set; }
public ITestClass TestClass { get; set; }
public ITypeInfo GenericArgument { get; set; }
/// <summary />
[Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")]
public GenericTestMethod()
{
}
public GenericTestMethod(ITestClass #class, IMethodInfo method, ITypeInfo genericArgument)
{
this.Method = method;
this.TestClass = #class;
this.GenericArgument = genericArgument;
}
public void Serialize(IXunitSerializationInfo info)
{
info.AddValue("MethodName", (object) this.Method.Name, (Type) null);
info.AddValue("TestClass", (object) this.TestClass, (Type) null);
info.AddValue("GenericArgumentAssemblyName", GenericArgument.Assembly.Name);
info.AddValue("GenericArgumentTypeName", GenericArgument.Name);
}
public static Type GetType(string assemblyName, string typeName)
{
#if XUNIT_FRAMEWORK // This behavior is only for v2, and only done on the remote app domain side
if (assemblyName.EndsWith(ExecutionHelper.SubstitutionToken, StringComparison.OrdinalIgnoreCase))
assemblyName = assemblyName.Substring(0, assemblyName.Length - ExecutionHelper.SubstitutionToken.Length + 1) + ExecutionHelper.PlatformSuffix;
#endif
#if NET35 || NET452
// Support both long name ("assembly, version=x.x.x.x, etc.") and short name ("assembly")
var assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == assemblyName || a.GetName().Name == assemblyName);
if (assembly == null)
{
try
{
assembly = Assembly.Load(assemblyName);
}
catch { }
}
#else
System.Reflection.Assembly assembly = null;
try
{
// Make sure we only use the short form
var an = new AssemblyName(assemblyName);
assembly = System.Reflection.Assembly.Load(new AssemblyName { Name = an.Name, Version = an.Version });
}
catch { }
#endif
if (assembly == null)
return null;
return assembly.GetType(typeName);
}
public void Deserialize(IXunitSerializationInfo info)
{
this.TestClass = info.GetValue<ITestClass>("TestClass");
string assemblyName = info.GetValue<string>("GenericArgumentAssemblyName");
string typeName = info.GetValue<string>("GenericArgumentTypeName");
this.GenericArgument = Reflector.Wrap(GetType(assemblyName, typeName));
this.Method = this.TestClass.Class.GetMethod(info.GetValue<string>("MethodName"), true).MakeGenericMethod(GenericArgument);
}
}
2) You need to write your own discoverer for generic methods, it has to be subclass of IXunitTestCaseDiscoverer
// assuming namespace Contosco
public class GenericMethodDiscoverer : IXunitTestCaseDiscoverer
{
public GenericMethodDiscoverer(IMessageSink diagnosticMessageSink)
{
DiagnosticMessageSink = diagnosticMessageSink;
}
protected IMessageSink DiagnosticMessageSink { get; }
public IEnumerable<IXunitTestCase> Discover(ITestFrameworkDiscoveryOptions discoveryOptions,
ITestMethod testMethod, IAttributeInfo factAttribute)
{
var result = new List<IXunitTestCase>();
var types = factAttribute.GetNamedArgument<Type[]>("Types");
foreach (var type in types)
{
var typeInfo = new ReflectionTypeInfo(type);
var genericMethodInfo = testMethod.Method.MakeGenericMethod(typeInfo);
var genericTestMethod = new GenericTestMethod(testMethod.TestClass, genericMethodInfo, typeInfo);
result.Add(
new XunitTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(),
genericTestMethod));
}
return result;
}
}
3) Finally you can make your attribute for generic methods and hook it to your custom discoverer by XunitTestCaseDiscoverer attribute
// assuming namespace Contosco
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
[XunitTestCaseDiscoverer("Contosco.GenericMethodDiscoverer", "Contosco")]
public sealed class GenericMethodAttribute : FactAttribute
{
public Type[] Types { get; private set; }
public GenericMethodAttribute(Type[] types)
{
Types = types;
}
}
Usage:
[GenericMethod(new Type[] { typeof(double), typeof(int) })]
public void TestGeneric<T>()
{
Assert.Equal(typeof(T), typeof(double));
}
private static void Main(string[] args)
{
var messageType = typeof (SampleHandler1);
var genericType = typeof (IConsume<>).MakeGenericType(messageType);
var genericArguments = genericType.GetGenericArguments();
var consumeMethod = genericType.GetMethod("Consume");
var constructorInfo = genericArguments[0].GetConstructor(Type.EmptyTypes);
var classObject = constructorInfo.Invoke(new object[] {});
var argsx = new object[] {new SampleMessage {Name = "sample message"}};
consumeMethod.Invoke(classObject, argsx);
}
public interface IConsume<in T> where T : class
{
void Consume(T message);
}
public class SampleHandler1 : IConsume<SampleMessage>
{
public SampleHandler1()
{
Debugger.Break();
}
public void Consume(SampleMessage message)
{
Debugger.Break();
Console.WriteLine("Message consume: " + message.Name);
}
}
public interface IBaseMessage
{
}
public class SampleMessage : IBaseMessage
{
public string Name { get; set; }
}
I tried looking here but I cant find specific solution. As MSDN explains
obj
Type: System.Object
The object on which to invoke the method or constructor. If a method is static, this argument is ignored. If a constructor is static, this argument must be null or an instance of the class that defines the constructor.
classObject is an instance of constructor, right? Why it throws an exception:
That doesn't seem right. Let's analyze what's happening here:
var messageType = typeof (SampleHandler1);
//simple enough, Type -> SampleHandler1
var genericType = typeof (IConsume<>).MakeGenericType(messageType);
//so genericType is a Type -> IConsume<SampleHandler1>
var genericArguments = genericType.GetGenericArguments();
//there's only one, but Type[] { Type -> SampleHandler1 }
var consumeMethod = genericType.GetMethod("Consume");
//MethodInfo -> IConsume<SampleHandler1>.Consume(SampleHandler1)
var constructorInfo = genericArguments[0].GetConstructor(Type.EmptyTypes);
//ConstructorInfo -> SampleHandler1..ctor()
var classObject = constructorInfo.Invoke(new object[] {});
//new SampleHandler1()
var argsx = new object[] {new SampleMessage {Name = "sample message"}};
//object[] { SampleMessage }
consumeMethod.Invoke(classObject, argsx);
//((IConsume<SampleHandler1>)classObject).Consume( SampleMessage ) -- oops?
So classObject is an SampleHandler1, but you're trying to invoke IConsume<SampleHandler1>.Consume(SampleHandler1) and worse yet give it a SampleMessage as an argument.
I think you meant to create a SampleHandler1, and invoke IConsume<SampleMessage>.Consume(SampleMessage) on it:
var messageType = typeof(SampleMessage);
var genericType = typeof(IConsume<>).MakeGenericType(messageType);
var consumeMethod = genericType.GetMethod("Consume");
var handlerType = typeof(SampleHandler1);
var constructorInfo = handlerType.GetConstructor(Type.EmptyTypes);
var classObject = constructorInfo.Invoke(new object[] {});
var argsx = new object[] {new SampleMessage {Name = "sample message"}};
consumeMethod.Invoke(classObject, argsx);
I'm not sure, but based on all of the components you have in your question, I suspect that you are looking for something more like this:
using System;
public class Program
{
public static void Main()
{
var handlerType = typeof (SampleHandler1);
var genericType = handlerType.GetInterface("IConsume`1");
var genericArguments = genericType.GetGenericArguments();
var consumeMethod = genericType.GetMethod("Consume");
var handlerConstructorInfo = handlerType.GetConstructor(Type.EmptyTypes);
var handler = handlerConstructorInfo.Invoke(new object[] {});
var messageConstructorInfo = genericArguments[0].GetConstructor(Type.EmptyTypes);
var messageObject = messageConstructorInfo.Invoke(new object[] {});
((IBaseMessage)messageObject).Name = "Sample Message";
var argsx = new object[] {messageObject};
consumeMethod.Invoke(handler, argsx);
}
}
public interface IConsume<in T> where T : class, IBaseMessage
{
void Consume(T message);
}
public class SampleHandler1 : IConsume<SampleMessage>
{
public SampleHandler1()
{
Console.WriteLine("SampleHandler1 constructed");
}
public void Consume(SampleMessage message)
{
Console.WriteLine("Message consume: " + message.Name);
}
}
public interface IBaseMessage
{
string Name { get; set; }
}
public class SampleMessage : IBaseMessage
{
public string Name { get; set; }
}
Here is a working Dotnetfiddle of the above answer: https://dotnetfiddle.net/YFmmzk
The console output is:
SampleHandler1 constructed
Message consume: Sample Message
It looks like you were getting your handler and message types confused. You were trying to pass an instance of the handler itself to the consume method. More over, IBaseMessage was missing the Name property declaration.
UPDATE
Here is a cleaned up version of this answer:
public class Program
{
public static void Main()
{
var handler = new DynamicConstructor(typeof (SampleHandler1)).New();
invokeIConsumeFor(handler, "Sample Message");
}
private static void invokeIConsumeFor(object handler, string message)
{
var executer = new DynamicGenericInterfaceExecuter(handler, "IConsume`1");
var messageObject = executer.GetTypeArgumentConstructor(0, Type.EmptyTypes).New();
((IBaseMessage) messageObject).Name = message;
executer.Method("Consume", messageObject.GetType()).Call(messageObject);
}
}
public class DynamicGenericInterfaceExecuter
{
private object instance;
private Type genericInterfaceFromType;
private Type[] genericTypeArguments;
public DynamicGenericInterfaceExecuter(object instance, string interfaceName)
{
this.instance = instance;
this.genericInterfaceFromType = instance.GetType().GetInterface(interfaceName);
this.genericTypeArguments = this.genericInterfaceFromType.GetGenericArguments();
}
public MethodExecuter Method(string methodName, params Type[] parameterTypes)
{
return new MethodExecuter(this.instance, this.genericInterfaceFromType, methodName, parameterTypes);
}
public DynamicConstructor GetTypeArgumentConstructor(int typeArgumentIndex, params Type[] constructorParameterTypes)
{
return new DynamicConstructor(this.genericTypeArguments[typeArgumentIndex], constructorParameterTypes);
}
}
public class DynamicConstructor
{
private System.Reflection.ConstructorInfo constructor;
public DynamicConstructor(Type type, params Type[] constructorParameters)
{
this.constructor = type.GetConstructor(constructorParameters);
}
public object New(params object[] constructorArguments)
{
return this.constructor.Invoke(constructorArguments);
}
}
public class MethodExecuter
{
private object instance;
private System.Reflection.MethodInfo method;
public MethodExecuter(object instance, Type containerType, string methodName, Type[] methodParameters)
{
this.instance = instance;
this.method = containerType.GetMethod(methodName, methodParameters);
}
public void Call(params object[] arguments)
{
this.Invoke(arguments);
}
public object Invoke(params object[] arguments)
{
return this.method.Invoke(instance, arguments);
}
}
public interface IConsume<in T> where T : class, IBaseMessage
{
void Consume(T message);
}
public class SampleHandler1 : IConsume<SampleMessage>
{
public SampleHandler1()
{
Console.WriteLine("SampleHandler1 constructed");
}
public void Consume(SampleMessage message)
{
Console.WriteLine("Message consume: " + message.Name);
}
}
public interface IBaseMessage
{
string Name { get; set; }
}
public class SampleMessage : IBaseMessage
{
public string Name { get; set; }
}
And the dotnetfiddle: https://dotnetfiddle.net/n9WHZ2
Keep in mind that this is not type safe in the slightest, but that does not appear to be a concern in your question.
Hi i'm try to cast a generic to an Action with a unknown number and type of Parameters
at the moment it looks like:
public class subscriber
{
public subscriber()
{
new Subscription<Action>(a);
new Subscription<Action<string>>(b);
new Subscription<Action<int,string>>(c);
}
private void a() { }
private void b(string gg){}
private void c(int i, string g) { }
}
public class Subscription<T>
{
public T MyAction {get {retun _action;}}
public Type MyActionType {get;private set;}
public Subscription( T action )
{
MyAction = action;
MyActionType = action.GetType();
var gg = action.GetType().GetGenericArguments();// Contains the Sub generics
}
}
at the moment we know it will be an Action and we also know the Sub Types but how to put this all together
to execute my private void c(int i, string g) method
Final Goal
is to execute the Action from a Third-Class (which will contains a List<Subscription> ) when a Fourth-Class hand over some params
public abstract class SubscriptionBase
{
public abstract void ExecuteAction(params object[] parameters);
}
public class Subscription<T> : SubscriptionBase
{
private T _action;
public Subscription(T a)
{
_action = a;
}
public override void ExecuteAction(params object[] parameters)
{
(_action as Delegate).DynamicInvoke(parameters);
}
}
and you can use it like;
Action<int> func1 = (q) => q += 1;
Action<int, int> func2 = (q, w) => q += w;
Subscription<Action<int>> s1 = new Subscription<Action<int>>(func1);
Subscription<Action<int, int>> s2 = new Subscription<Action<int, int>>(func2);
List<SubscriptionBase> subscriptionBase = new List<SubscriptionBase>();
subscriptionBase.Add(s1);
subscriptionBase.Add(s2);
subscriptionBase[1].ExecuteAction(1, 2);
You can't do it that way. You can't put a Subscription<Action<int>> into the same list as Subscription<Action<string, Foo>>.
I suggest, you create an interface like the following and store those in your third class:
interface IActionExecutor
{
bool CanExecuteForParameters(params object[] parameters);
void Execute(params object[] parameters);
}
// Implementation for one parameter
// You need to create one class per additional parameter.
// This is similar to the Action delegates in the framework.
// You can probably extract a base class here that implements
// some of the repetitive pars
public class ActionExecutor<in T> : IActionExecutor
{
private Action<T> _action;
public ActionExecutor(Action<T> action)
{
if(action == null) throw new ArgumentNullException("action");
_action = action;
}
public bool CanExecuteForParameters(params object[] parameters)
{
if(parameters == null) throw new ArgumentNullException("action");
if(parameters.Length != 1) return false;
return parameters[0] is T;
}
public void Execute(params object[] parameters)
{
if(parameters == null) throw new ArgumentNullException("action");
if(parameters.Length != 1)
throw new ArgumentOutOfRangeException("action");
_action((T)parameters[0]);
}
}
In your third class you would have the list of IActionExecutors:
List<IActionExecutor> _subscriptions;
And you would use it like this:
public void Execute(params object[] parameters)
{
var matchingSubscriptions =
_subscriptions.Where(x => x.CanExecuteForParameters(parameters);
foreach(var subscription in matchingSubscriptions)
subscription.Execute(parameters);
}
To simplify the creation of those ActionExecutor instances, you can provide a factory class:
public static class ActionExecutor
{
public IActionExecutor Create(Action action)
{
return new ActionExecutor(action);
}
public IActionExecutor Create<T>(Action<T> action)
{
return new ActionExecutor<T>(action);
}
public IActionExecutor Create<T1, T2>(Action<T1, T2> action)
{
return new ActionExecutor<T1, T2>(action);
}
// ... and so on
}
Usage would now be like this:
_subscriptions.Add(ActionExecutor.Create(a));
_subscriptions.Add(ActionExecutor.Create(b));
_subscriptions.Add(ActionExecutor.Create(c));