I have an abstract class called Action which accepts a byte array and represents actions done by the client. I have several actions that are implemented from this class and they contain properties, like so:
[Action(ActionCode.Login)]
public class LoginAction : Action
{
public string Username { get; private set; }
public string Password { get; private set; }
public LoginAction(byte[] data)
: base(data)
{
this.Username = this.ReadMapleString();
this.Password = this.ReadMapleString();
}
}
I want to be able to defind a method using the actions like so:
public static void Login(LoginAction action)
So when I receive data from the client I can handle the actions based on the code received. However, I'm not sure how to use reflection to find the method that's associated with the action. I mean, I can find LoginAction using the code under, but I can't find the method that's using LoginAction as a parameter, which is the method I want to invoke.
I want to achieve something like:
public void OnRecieveData(ActionCode code, byte[] data)
{
// Using the ActionCode, call the specified action handler method.
// Login(new LoginAction(data));
}
I already know how to find classes that use the ActionCodeAttribute, but I'm not sure how to invoke it:
static IEnumerable<Type> GetTypesWithHelpAttribute(Assembly assembly) {
foreach(Type type in assembly.GetTypes()) {
if (type.GetCustomAttributes(typeof(HelpAttribute), true).Length > 0) {
yield return type;
}
}
}
Assuming there is a good reason for your design (see comments), you could find it with a bit of LINQ.
First, we need to find the type. We just get all types, filter their attributes by type (using OfType<>), and find the first class that has at least one ActionAttribute.
var type = System.Reflection.Assembly.GetCallingAssembly()
.GetTypes()
.Where
(
t => t.GetCustomAttributes()
.OfType<ActionAttribute>()
.Where( a => a.ActionCode == code)
.Any()
)
.Single();
Next to find the static member. We already know the type of the containing class. But we don't necessarily know the name. Tnen again, we do know the type of the first parameter, and that the call is static. Assuming there is always exactly one method that meets all of these criteria, we can use this:
var member = type
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where
(
m=> m.GetParameters()
.First()
.ParameterType == type
)
.Single();
Then we create the instance:
var instance = Activator.CreateInstance(type, new object[] { data });
And invoke it:
member.Invoke(null, new object[] { instance });
Full example:
static public void OnReceiveData(ActionCode code, byte[] data)
{
var type = System.Reflection.Assembly.GetCallingAssembly()
.GetTypes()
.Where
(
t => t.GetCustomAttributes()
.OfType<ActionAttribute>()
.Where( a => a.ActionCode == code)
.Any()
)
.Single();
var member = type
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where
(
m=> m.GetParameters()
.First()
.ParameterType == type
)
.Single();
var instance = Activator.CreateInstance(type, new object[] { data });
member.Invoke(null, new object[] { instance });
}
Working example on DotNetFiddle
What you can do when you want to execute actions based on a code is to create a dictionary with action objects:
public abstract class Action {
public abstract void Execute(byte[] data);
}
//...
Dictionary<ActionCode, Action> actions = new Dictionary<ActionCode, Action>();
//...
void ExecuteAction(ActionCode actionCode, byte[] data) {
actions[actionCode].Execute(data);
}
You can populate the actions dictionary in an appropriate way that solves your problem. If you want to load the implementations from external libraries, you could do the following (using your existing GetTypesWithHelpAttribute method):
void PopulateActions(Assembly assembly) {
foreach(Type actionType in GetTypesWithHelpAttribute(assembly)) {
var codeQry =
from attribute in type.GetCustomAttributes().OfType<ActionAttribute>()
select attribute.Code;
ActionCode code = codeQry.Single();
Action action = (Action)Activator.CreateInstance(type);
actions.Add(code, action);
}
}
That assumes you apply the HelpAttribute to the actions you want to load. You can also directly look for the ActionAttribute and drop the HelpAttribute altogether.
This solution keeps the reflection code where it belongs: when you load the actions from an external source. Your execution code is free from reflection, which improves maintainability and performance. You can also combine the dynamic load with static actions you add without reflection from your main module.
If you're happy to define methods using the same name in the same class for each of the different type of actions:
void Handle(LoginAction action);
void Handle(FooAction action);
void Handle(BarAction action);
And you've constructed your ...Action object of the right type, you can call it using:
Handle((dynamic)action)
You can even afford the explicit case if you just do
dynamic dynAction = action;
Handle(dynAction);
And yes, the correct overload will be determined at runtime based on the type of the action.
Related
I am learning generics in C#. So this may be simple for experienced folks.
I have 71 different models and I want to be able to generically store data from CSV into them.
The processing part is not super hard, I have this method signature:
private static async Task ProcessFileAsync<T>(string currentFile) where T : class, new()
The hard part is calling it. I have one CSV file for each model that I want to place data into. The Name of the CSV file is identical to the Model's name (ex: Product.csv would correspond to the Product model).
Ideally, I would like to just send the name in the caller, but I am getting a "X is a variable but is used like a type" Compiler error.
I could have a massive switch statement to solve this issue, but that seems relatively wasteful.
Any assistance would be appreciated.
Put another way, I could do the following:
switch(justFName)
{
case "Address":
_ = ProcessFileAsync<Address>(ci.FullName);
break;
case "Currency":
_ = ProcessFileAsync<Currency>(ci.FullName);
break;
...
...
...And so on
...
...
default:
//No method for this file name
break;
}
instead I would like to have something like this:
_ = ProcessFileAsync<justFName>(ci.FullName);
If you can somehow determine all classes you need to handle from your assembly (personally I like to mark them with specially created attribute), then Reflection and Expression Trees to the rescue:
public class Address { }
public class MethodHolder // Just dummy class to hold your process action
{
public static async Task ProcessFileAsync<T>(string currentFile) where T : class, new()
{
Console.WriteLine(currentFile);
}
}
public static class Processor
{
private static readonly Dictionary<string, Action<string>> _dict;
static Processor()
{
var types = typeof(Address).Assembly.GetTypes()
// filter your types correctly here somehow
// JIC do not forget to verify that they satisfy
// your generic constraints
.Where(t => t.Name == "Address");
_dict = types.ToDictionary(t => t.Name, BuildAction);
}
private static Action<string> BuildAction(Type t)
{
var method = typeof(MethodHolder).GetMethod(nameof(MethodHolder.ProcessFileAsync))
.MakeGenericMethod(t);
var param = Expression.Parameter(typeof(string));
return Expression.Lambda<Action<string>>(
Expression.Call(method, param),
param)
.Compile();
}
// TODO: add some nice handling for keys not in dictionary
public static void Process(string key, string value) => _dict[key](value);
}
And usage: Processor.Process(nameof(Address), "testfilename"); (nameof just for the sake of example)
The #GuruStron's answer is very good. Another way of achieving what you need is only using Reflection. However, like #GuruStrong suggest, it's good for you that include an annotation in the classes where the search will be performed, or put them in a single assembly. The following code works only if these classes are in the same assembly.
#region These classes must be in the same assembly
public class Address {
}
public class Currency {
}
#endregion
class Program {
static async Task Main(string[] args) {
var justFName = "Currency";
var fullName = "name";
var type = typeof(Address).Assembly.GetTypes()
.Single(x => x.Name == justFName);
var method = typeof(Program).GetMethod(nameof(ProcessFileAsync),
BindingFlags.NonPublic | BindingFlags.Static)
.MakeGenericMethod(type);
await (Task)method.Invoke(null, new object[] { fullName });
Console.ReadLine();
}
private static async Task ProcessFileAsync<T>(string currentFile) where T : class, new() {
Console.WriteLine(currentFile);
}
}
We are creating a configuration API for some of our internal projects. The API is replacing an attribute-based configuration. The new API involves specifying methods within a class along with the options that go with it.
public static class Configuration<T> {
public static void ForMethod(Expression<Action<T>> methodExpression, Options options) { //Option-1
MethodInfo method = GetMethod(methodExpression);
// do something with options
}
public static void ForMethod<TResult>(Expression<Func<T,TResult>> methodExpression, Options options) { //Option-2
MethodInfo method = GetMethod(methodExpression);
// do something with options
}
private static MethodInfo GetMethod(Expression expression) {
// there's other checks done, but this is what it boils down to
var methodCall = (MethodCallExpression) expression.Body;
return methodCall.Method;
}
}
class MyClass {
void MyMethod() { ... }
}
Configuration<MyClass>.ForMethod(x => x.MyMethod(), ... );
This is great except for when the methods themselves have parameters. IE
class MyClass2 {
void MyMethod2(int x) { }
int SomeOtherMethod(int x) { return x*x; }
}
These can be handled by expanding the configuration class like:
public static void ForMethod<TResult, TIn>(Expression<Func<T,TIn,TResult>> methodExpression, ...) { //Option-3
}
// and then called:
Configuation<MyClass2>.ForMethod<int,int>((x,y) => x.SomeOtherMethod(y), ...);
As more parameters get added it becomes a bit more clumsy. Technically we don't care about the parameters themselves, so long as we select the correct method. We could get around this by simply specifying everything as Option-1 (since we can also ignore the return type and specify everything as Actions) and just specifying the parameters using the default keyword, ie:
Configuration<MyClass2>.ForMethod(x => x.SomeOtherMethod(default(int)), ...)
However, this gets quite ugly.
Is there any way with type inference (or some other magic) to select the appropriate method without needing to explicitly state the parameter types? My hope would be something like this (although this do not work)
Configuration<MyClass2>.ForMethod((x,y) => x.SomeOtherMethod(y), ...) // Option 3 with return type and type of input inferred
or even
Configuration<MyClass2>.ForMethod((x,y) => x.SomeOtherMethod, ...) // Option 3 with return type and input 1 inferred + as method group
My gut tells me that this is not possible as I haven't been able to find any way around this online. My hope is that perhaps with a combination of extension methods and type inference this could be made possible, but I'm having difficulty finding anything that compiles. Is this even possible?
I have the following type hierarchy:
public abstract class Controller {}
public sealed class PersonController : Controller {}
public sealed class OrderController : Controller {}
I also have a method that resolves the instance of a given type on demand (think of it as a layman's IOC):
private void Resolve<T>(Func<T>[] controllerFactories) where T : Controller
{
Array.ForEach(controllerFactories, x =>
{
// This returns Controller instead of the child class
// I need to know the actual type so that I can do other stuff
// before resolving the instance.
Console.WriteLine(x.Method.ReturnType);
var controllerInstance = x();
});
}
I need to figure out the type of T in Func<T> but when I try:
void Main()
{
var factories = new Func<Controller>[] {
() => new PersonController(),
() => new OrderController()
};
Resolve(factories);
}
I get Controller instead of the PersonController and OrderController.
Any ideas?
Update:
I thank everyone for providing such detailed answers and examples. They prompted me to rethink the API and here is what I finally came up with which serves what I wanted to accomplish:
public interface IResolver<T>
{
void Register<TKey>(Func<TKey> factory) where TKey : T;
T Resolve(Type type);
void ResolveAll();
}
public sealed class ControllerResolver : IResolver<Controller>
{
private Dictionary<Type, Func<Controller>> _factories = new Dictionary<Type, Func<Controller>>();
public void Register<TKey>(Func<TKey> factory) where TKey : Controller
{
_factories.Add(typeof(TKey), factory);
}
public Controller Resolve(Type type) => _factories[type]();
public void ResolveAll()
{
foreach (var pair in _factories)
{
// Correctly outputs what I want
Console.WriteLine(pair.Value.Method.ReturnType);
}
}
}
And here are some usage examples:
void Main()
{
var resolver = new ControllerResolver();
resolver.Register(() => new PersonController());
resolver.Register(() => new OrderController());
resolver.ResolveAll();
resolver.Resolve(typeof(PersonController));
}
Each method has a return type stored in the assembly, you specified that your methods' return type is Controller. This is the only thing that is guaranteed as an information (That is what we know without executing the method - Also at compile time)
So we know that the method should return Controller or anything that derive from that. We can never know what is the run-time type until we actually call that method.
As an example, what if the method has a code like:
var factories = new Func<Controller>[] {
() =>
{
if ( DateTime.Now.Second % 2 == 0 )
return new OrderController();
else
return new PersonController();
}
};
If you need to get the run-time type of the object returned, then you need to:
var controllerInstance = x();
Console.WriteLine(controllerInstance.GetType().Name);
Lambdas in C# get their type based on the expression they are assigned to. For example, this:
Func<string, string> d = x => x;
Makes the lambda x => x a string => string function, on the grounds that this is what the variable d expects.
Why is this relevant? Because in the following code, you're creating a similar expectation for the following lambdas:
var factories = new Func<Controller>[] {
() => new PersonController(),
() => new OrderController()
};
The expectation is that these lambdas are of the Func<Controller> type. If the compiler was looking at the lambda bodies for the type, it would arrive at a different conclusion, specifically that one is a Func<PersonController> and the other is a Func<OrderController>, both of which are kinds of Func<Controller>. But the compiler doesn't look at the body for the type, it looks at the variable and the "variable" in this case is the array slot.
Thus, the compiler will generate these delegates not as these methods:
PersonController Delegate1()
{
return new PersonController();
}
OrderController Delegate2()
{
return new OrderController();
}
But as these methods:
Controller Delegate1()
{
return new PersonController();
}
Controller Delegate2()
{
return new OrderController();
}
Therefore, by the time these delegates are inserted into the array, only the type information of the signatures remain, while the actual type information of the lambda bodies is lost.
But there is one way to maintain the information about the lambda bodies, and then inspect that information instead of the delegate signatures. You can use of expression trees, which are a way to tell the compiler to treat code as data and essentially generate tree objects that represent the lambda, instead of the actual method that implements the lambda.
Their syntax is nearly identical to that of lambdas:
var factories = new Expression<Func<Controller>>[] {
() => new PersonController(),
() => new OrderController()
};
The difference is that now these objects aren't functions, but rather representations of functions. You can traverse those representation and very easily find the type of their top-level expression:
var t0 = factories[0].Body.Type; // this is equal to typeof(PersonController)
var t1 = factories[1].Body.Type; // this is equal to typeof(OrderController)
Finally, you can also turn these representations into actual functions, if you also intend to run them:
Func<Controller>[] implementations = factories.Select(x => x.Compile()).ToArray();
The problem is that the same T has to apply to every type in your array. In
private void Resolve<T>(Func<T>[] controllerFactories) where T : Controller
{
Array.ForEach(controllerFactories, x =>
{
// This returns Controller instead of the child class
Console.WriteLine(x.Method.ReturnType);
var controllerInstance = x();
});
}
what is your T? Is it PersonController? Is it OrderController? To make it fit your use case, it can only be a class that is a superclass of both PersonController and OrderController, while being a subclass of Controller. Therefore T can only be one class: Controller (assuming the two classes don't have a common base class that extends Controller). There is no point in making this class generic. It is exactly the same as this:
private void Resolve(Func<Controller>[] controllerFactories)
{
Array.ForEach(controllerFactories, x =>
{
Console.WriteLine(x.Method.ReturnType);
var controllerInstance = x();
});
}
Instead of an array of Func<Controller> instances, it may make more sense to pass in a dictionary keyed on your return type:
var factories = new Dictionary<Type, Func<Controller>> {
[typeof(PersonController)] = () => new PersonController(),
[typeof(OrderController)] = () => new OrderController()
};
An instance of Func<Controller> simply does not contain enough information to determine the type of its return value on its own: not without evaluating it. And even if you evaluate it, remember that a function is a black box. Just because it returns a PersonController once doesn't mean that it won't return an OrderController the next time you invoke it.
This is how it works, when you are instantiating your array, you are specifying that it would be storing Func objects of type Controller, now you are instantiating the sub types objects inside the array of Func, the Resolve, does not know what is the actual type unless you call GetType() to determine the actual type of it, it just knows that the T will be of type Controller because of the constraint i.e where T : Controller
i would like to convert a method that already does the job
(only if you instantiate the class within its body)
to one that will accept the subjected class as a passed parameter...somehow.
i have tried to get that result by my self by trial and error ,
(mostly trial ..and errors , lots...)
but no success (:
this first class is within my helpers classes (the extensions namespace)
public static List<string> AnyClassFieldsValuesAsList<T>(this T Clss, string nestedName)
{
// i know how to get this to work... if an instace of the class is declared
// only here i would like to have any given class...as passed parameter
// couple of tests to get a hold of the passed Class...no success (:
var T1 = typeof(T.GetType());
var T2 = Clss.GetType();
return typeof(Clss.GetType()).GetFields(); //ToList<string>();
}
this is the subjected class (that i have stored in same helpers file) to hold strings representing style-fonts
public class FontNames
{
public readonly string Aharoni = "Aharoni",
Andalus = "Andalus",
AngsanaNew = "Angsana New",
AngsanaUPC = "AngsanaUPC",
Aparajita = "Aparajita";
//.....etc'
}
now within the code behind of the current project i want to be able to do something like
//imports of extensions and my style namespaces ....
// then somewhere after Page_Load()...
var instnceOfFnames = new FontNames();
list<string> FontsLst= instnceOfFnames.AnyClassFieldsValuesAsList(.....);
parameters in signature of AnyClassFieldsValuesAsList() at the top are just for testing,
as i couldn't work with them, i am not sure that they are the ones that i should pass.
what is the correct syntax to achive the results ?
As far as I understood, you need to get the fields' values without an instanse of the class. I suppose, you should declare those fields as public readonly static .... Then you'll be able to utilize the following method:
public static IEnumerable<string> GetFields<T>()
{
Type type = typeof(T);
return type.GetFields(BindingFlags.Static | BindingFlags.Public)
.Where(f => f.FieldType == typeof(string))
.Select(f => (string)f.GetValue(null));
}
like this:
foreach (string f in GetFields<FontNames>())
Console.WriteLine(f);
Based on comments:
For the current issue (as far as I understand it), it seems to be superfluos, as static fields do not need instances to be accessed. But at least it can give some ideas
public static IEnumerable<string> GetFields<T>(this T value)
{
Type type = value.GetType();
//...all the same as above
}
To get the same result it's enough to have the same without generics
public static IEnumerable<string> GetFields(this object value)
{
Type type = value.GetType();
//...all the same as above
}
I'd like to create a custom attribute to apply to any method within a class, and then, from outside that class, access the method inside the class that has been 'tagged' with the attribute to call the tagged method as if it were a delegate.
e.g.
public delegate string MethodCall( string args);
and
public class MethodAttribute : System.Attribute
{
public MethodCall callback;
...
}
and
class TypeWithCustomAttributesApplied {
[Method]
public string DelegateMethod(string args) {
...
}
}
and then
void callMethods(string args) {
TypeWithCustomAttributesApplied myObj = new TypeWithCustomAttributesApplied();
DelegateMethod method = MyCustomerHelper.GetMarkedMethod(myObj)
method(args);
}
or perhaps
void callMethods(string args) {
TypeWithCustomAttributesApplied myObj = new TypeWithCustomAttributesApplied();
MethodAttribute methodAtrib = MyCustomerHelper.GetMarkedMethod(myObj)
methodAtrib.callback(args);
}
What I'm ultimately trying to achieve is a Custom Attribute that I can use to 'Mark' Ajax Entry points in arbitary classes, then with a Helper Class, pass the 'Ajax Enabled' control to the helper which identifies which method in the control to call, and hands it the ajax data from the client. I'm not so great with delegates anyway, but I generally understand how to apply custom attributes, but not sure how to 'capture' the method I'm 'tagging'
I could probably manage my task some other way, but I'm trying my hand at attributes, so I'd like to get this method working first.
My Final Solution :
public void CheckAjax(object anObject, string args)
{
MethodInfo[] methods = anObject.GetType().GetMethods();
foreach (MethodInfo method in methods)
{
object[] attributes = method.GetCustomAttributes(true);
bool containsAttribute = (from attribute in attributes
where attribute is AjaxableAttribute
select attribute).Count() > 0;
if (containsAttribute)
{
string result_method = (string)method.Invoke(anObject, new object[] { args });
Log.Write(string.Format("The Result from the method call was {0} ", result_method));
}
}
}
You can use reflection and LINQ:
IEnumerable<MethodInfo> methods = myObj.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public).Where(method => method.GetCustomAttributes(typeof(MethodAttribute), true).Length == 1).ToList();
string args = "some args";
foreach(MehtodInfo method in methods)
{
method.Invoke(myObj, new object[] { args });
}
What you have to do to get this methods is the following:
MethodInfo[] methods = typeof(TypeWithCustomAttributesApplied).GetMethods();
foreach (MethodInfo method in methods)
{
object[] attributes = method.GetCustomeAttributes(true);
bool containsAttribute = (from attribute in attributes
where attribute is MethodAttribute
select attribute).Count() > 0;
if (containsAttribute)
// add attribute to list to return later
}
After returning the methods you can call the methods with
method.Invoke(/* parameters *);
Hope this helps.
I think your solution for that would be Reflection to find the method with the attribute you want.
As shown somewhere else you can do
from m in type.GetMethods()
where m.GetCustomAttributes().OfType<YourAttribute>().Any()
select m
Once you get you MethodInfo via reflection "GetMethods where has attribute" the following code shows how you construct a delegate based on a MethodInfo. In this example it is some Action<> but it could be a different type. Here "parameterType" is a type provided from the outside. The resulting delegate can be cast to the type you require. "target" is the instance this delegate will be called upon.
var fittingDelegateType = typeof(Action<>).MakeGenericType(parameterType);
var #delegate = Delegate.CreateDelegate(fittingDelegateType, target, info);