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.
Related
I try to create a dynamic method in .NET which has an "in" parameter, but it throws an exception in the EmitCall line:
public struct Context
{
public string MyString { get; set; }
}
public interface IHandler<T>
{
void MyMethod(in Context context);
}
class MyMessage
{
}
class MyClass : IHandler<MyMessage>
{
public void MyMethod(in Context context)
{
}
}
class ContextMethodInvoker
{
private readonly InvokeHandler _invoker;
private delegate void InvokeHandler(object handler, in Context context);
public ContextMethodInvoker(MethodInfo methodInfo)
{
var dynamicMethod = new DynamicMethod(string.Empty,
typeof(void), new[] { typeof(object), typeof(Context).MakeByRefType() });
dynamicMethod.DefineParameter(1, ParameterAttributes.None, "handler");
dynamicMethod.DefineParameter(2, ParameterAttributes.In, "context");
var il = dynamicMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.EmitCall(OpCodes.Callvirt, methodInfo, null);
il.Emit(OpCodes.Ret);
_invoker = (InvokeHandler)dynamicMethod.CreateDelegate(typeof(InvokeHandler));
}
public void Invoke(object handler, in Context context)
{
_invoker.Invoke(handler, in context);
}
}
And:
var messageHandlerType = typeof(IHandler<>);
var messageType = typeof(MyMessage);
var type = typeof(MyClass);
var interfaceType = messageHandlerType.MakeGenericType(messageType);
var contextMethod = new ContextMethodInvoker(
type.GetInterfaceMap(interfaceType).TargetMethods.SingleOrDefault()
);
var tm = new Context
{
MyString = "test"
};
contextMethod.Invoke(Activator.CreateInstance(type), tm);
What am I doing wrong? When I remove the "in" keywords and the MakeByRefType then it works.
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));
}
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!
I want to generate the right object with one code line and not a switch case because always when a new device is added I have to add a new line.
Is it possible to do that in one line without switch case?
public static Device GetDevice(Device.enumDevice TypeOfDevice, string alias)
{
// Create the Object with using reflection
switch (TypeOfDevice)
{
case Device.enumDevice.A34411:
return new A34411(string alias);
break;
case Device.enumDevice.N5744:
return new N5744(string alias);
break;
default:
throw new NotImplementedException();
}
return null;
}
You could store the factory methods as delegates in a dictionary
private static Dictionary<Device.enumDevice, Func<string, Device>> _factoryDict =
new Dictionary<Device.enumDevice, Func<string, Device>>{
[Device.enumDevice.A34411] = (alias) => new A34411(alias),
[Device.enumDevice.N5744] = (alias) => new N5744(alias),
};
...
public static Device GetDevice(Device.enumDevice TypeOfDevice, string alias)
{
if (_factoryDict.TryGetValue(TypeOfDevice, out var factory)) {
return factory(alias);
}
throw new NotImplementedException();
// No retun statement here, as it would be unreachable because of the throw statement.
}
Or, using reflection:
const string deviceNameSpace = "MyName.MyProject.Devices.";
public static Device GetDevice(Device.enumDevice deviceType, string alias)
{
string typeName = deviceNameSpace + deviceType.ToString();
Type type = Type.GetType(typeName, throwOnError: true);
return (Device)Activator.CreateInstance(type, alias);
}
An elegant approach would be to use Dependency Injection with "Named Type Registrations"
Fast, but not quite a complete example:
public abstract class Device
{
protected Device(string alias)
{
Alias = alias;
}
public string Alias { get; }
}
public class A1 : Device
{
public A1(string alias) : base(alias) { }
}
public class A2 : Device
{
public A2(string alias) : base(alias) { }
}
class DeviceAttribute : Attribute
{
public DeviceAttribute(Type type)
{
Type = type;
}
public Type Type { get; }
}
public enum DeviceEnum
{
[Device(typeof(A1))]
A1,
[Device(typeof(A2))]
A2
}
public static class DeviceEnumExtension
{
public static Device GetInstance(this DeviceEnum obj, string alias)
{
var member = typeof(DeviceEnum).GetMember(obj.ToString());
if (member[0].GetCustomAttributes(typeof(DeviceAttribute), false)[0] is DeviceAttribute deviceAttr)
{
var ctor = deviceAttr.Type.GetConstructor(new[] {typeof(string)});
return ctor.Invoke(new object[] {alias}) as Device;
}
return null;
}
}
public class UnitTest1
{
[Fact]
public void Test1()
{
// Arrange
var a1 = DeviceEnum.A1;
var a2 = DeviceEnum.A2;
// Act
var instanceA1 = a1.GetInstance("A1");
var instanceA2 = a2.GetInstance("A2");
// Assert
Assert.Equal(typeof(A1), instanceA1.GetType());
Assert.Equal(typeof(A2), instanceA2.GetType());
Assert.Equal("A1", instanceA1.Alias);
Assert.Equal("A2", instanceA2.Alias);
}
}
I have a method which returns the base type, I want an overload (preferably, but a new method would be fine) that returns a parent class instead, so that I could do something like:
DynamicClass cl = new DynamicClass(...);
var dict = cl.CreateObject<DynamicDictionary>("ClassName");
dict.CallAddOnMethod("test");
Here is the code I have tried, but I can't seem to get to DynamicDictionary
public sealed class DynamicClass : DynamicBaseClass
{
public DynamicObject CreateObject(params object[] args)
{
Debug.Assert(args.Length > 0);
object handle = InvokeMethod(CreateObjectMethod, args);
return new DynamicObject(bcAssembly, (string)args[0], handle);
}
public T CreateObject<T>(params object[] args)
{
Debug.Assert(args.Length > 0);
object handle = InvokeMethod(CreateObjectMethod, args);
DynamicObject item = new DynamicObject(bcAssembly, (string)args[0], handle)
return (T)item; // cant do this?
}
}
public class DynamicObject : DynamicBaseClass
{
private const string CallMethod = "Call";
public DynamicObject(Assembly assembly, string parentName, object classHandle)
: base(assembly, parentName, classHandle)
{
}
public object Call(string methodName, params object[] paramList)
{
return InvokeMethod(CallMethod, paramList);
}
}
public sealed class DynamicObject<T> : DynamicObject
{
private const string CallMethod = "Call";
public DynamicObject(Assembly assembly, string parentName, object classHandle)
: base(assembly, parentName, classHandle)
{
}
}
public sealed class DynamicDictionary : DynamicObject
{
private const string AddOnMethod = "AddOn";
public DynamicDictionary(Assembly assembly, string parentName, object classHandle)
: base(assembly, parentName, classHandle)
{
}
public int CallAddOnMethod(string name)
{
return (int)Call(AddOnMethod, name);
}
}
In your template, you should require that the type is a subclass of your dynamic object:
I.e.
var blah_b = test<b>();
blah_b.Dump();
var blah_c = test<c>();
blah_c.Dump();
-
public T test<T>() where T : a
{
var item = Activator.CreateInstance(typeof(T));
return (T)(item);
}
public class a
{
}
public class b : a
{
}
public class c : a
{
}
One alternative is to load codes, compile the codes and execute the compiled results dynamically. The following example uses a private string as a template code source (you may load it from a text file):
using Microsoft.CSharp;
public class MyTemplate : IDisposable {
private const string _SourceCodeTemplate = #"
using System;
public class {0} {{ // {{0}} Class name
public {0} CreateInstance()
{{
{0} instance;
//... some codes here to create instance
return instance;
}}
public {1} {2}() {{ // {{1}} method result, {{2}} method name
{1} result;
// some codes here for the result
return result;
}}
}}";
public T CallInstanceMethod<T>(string className, string callMethod,
string callMethodResult,
params object[] paramList) {
string codes;
// build codes dynamically
codes = string.Format(_SourceCodeTemplate, className,
callMethodResult, callMethod);
CSharpCodeProvider codeProvider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = false;
parameters.GenerateInMemory = true;
parameters.IncludeDebugInformation = false;
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
{
parameters.ReferencedAssemblies.Add(asm.Location);
}
// compile the codes
CompilerResults cr= codeProvider.CompileAssemblyFromSource(parameters, codes);
var csInstance = cr.CompiledAssembly.CreateInstance(className);
Type type = csInstance.GetType();
var methodForInstance = type.GetMethod(callMethod);
object value;
value = _methodForInstance.Invoke(csInstance, paramList);
return (T)value;
}
}