Reputation:
I have multiple instances of methods in classes that I need to invoke and write quickly without adding them to my main function. How would that be completed with an attribute?
e.g. I have a lot of different classes that have a method called 'invoke'. I want to add a custom attribute that I can add to this method and then call the invoke method on each one of these classes in a different method called 'invoke all'.
Something like looks like this, but functional.
public class main_class
{
public void invoke_all()
{
// call all the invokes
}
}
public class test1
{
[invoke]
public void invoke()
{
Console.WriteLine("test1 invoked");
}
}
public class test2
{
[invoke]
public void invoke()
{
Console.WriteLine("test2 invoked");
}
}
Upvotes: 4
Views: 22659
Reputation: 1992
To call a method, you need to instantiate a class. To instantiate a class, you need to know the type.
So we need to
Invoke
attributeLet's first define the attribute :
public class InvokeAttribute : Attribute
{
}
You can use this attribute to mark the methods:
public class TestClass1
{
[Invoke]
public void Method1()
{
Console.WriteLine("TestClass1->Method1");
}
[Invoke]
public void Method2()
{
Console.WriteLine("TestClass1->Method2"););
}
}
public class TestClass2
{
[Invoke]
public void Method1()
{
Console.WriteLine("TestClass2->Method1");
}
}
Now how to find and call these methods:
var methods = AppDomain.CurrentDomain.GetAssemblies() // Returns all currenlty loaded assemblies
.SelectMany(x => x.GetTypes()) // returns all types defined in this assemblies
.Where(x => x.IsClass) // only yields classes
.SelectMany(x => x.GetMethods()) // returns all methods defined in those classes
.Where(x => x.GetCustomAttributes(typeof(InvokeAttribute), false).FirstOrDefault() != null); // returns only methods that have the InvokeAttribute
foreach (var method in methods) // iterate through all found methods
{
var obj = Activator.CreateInstance(method.DeclaringType); // Instantiate the class
method.Invoke(obj, null); // invoke the method
}
The snippet above will check all loaded assemblies. The linq query
InvokeAttribute
This gives us a list of MethodInfo
s. A method info contains the DeclaringType
, which is the class the method was declared in.
We can use Activator.CreateInstance
to instantiate an object of this class. This will only work if the class has a public constructor without parameters.
Then we can use the MethodInfo
to invoke the method on the previously created class intance. This will only work if the method doesn't have parameters.
Upvotes: 10
Reputation: 3651
You can use static event instead of Attribute
public static class Events
{
public static event EventHandler OnInvoke;
public static void Run()
{
OnInvoke?.Invoke(null, EventArgs.Empty);
}
}
Subscribe on this event in the class constructor
public class Customer
{
public Customer()
{
Events.OnInvoke += (sender, args) => Call();
}
}
But Don't forget to Unsubscribe from this event otherwise all your objects will never be disposed
This will call your code on each Instantiated (existing) object in the application. Means if you have 2 objects of type test1
than Console.WriteLine("test1 invoked");
will be executed twice
Upvotes: 1
Reputation: 6095
You can create Interface based solution for your requirement.
I have modified your code and achieve what you want with this way.
namespace ConsoleApplication1
{
public class main_class
{
static void Main(string[] args)
{
main_class.invoke_all();
}
public static void invoke_all()
{
// call all the invokes
// Help : https://stackoverflow.com/questions/26733/getting-all-types-that-implement-an-interface
foreach (Type mytype in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
.Where(mytype => mytype.GetInterfaces().Contains(typeof(IInvokeAll))))
{
mytype.GetMethod("invoke").Invoke(Activator.CreateInstance(mytype, null), null);
}
//wait for user input
Console.ReadLine();
}
}
interface IInvokeAll
{
void invoke();
}
public class test1 : IInvokeAll
{
//[invoke]
public void invoke()
{
Console.WriteLine("test1 invoked");
}
}
public class test2 : IInvokeAll
{
//[invoke]
public void invoke()
{
Console.WriteLine("test2 invoked");
}
}
}
Upvotes: 1
Reputation: 573
Here is a sample code to achieve what you intend using delegates.
public class main_class
{
private static main_class instance;
public delegate void MethodInvoker();
public MethodInvoker MyInvoker { get; set; }
public static main_class Instance
{
get
{
if (instance == null)
{
instance = new main_class();
}
return instance;
}
}
private main_class() { }
public void invoke_all()
{
MyInvoker();
}
}
public class test1
{
public test1()
{
main_class.Instance.MyInvoker += invoke;
}
public void invoke()
{
Console.WriteLine("test1 invoked");
}
}
public class test2
{
public test2()
{
main_class.Instance.MyInvoker += invoke;
}
public void invoke()
{
Console.WriteLine("test2 invoked");
}
}
So now, wherever you call main_class.Instance.invoke_all();
it will invoke all the methods.
Upvotes: -2
Reputation: 151588
The process is simple using reflection: find all types t
of interest, get all methods m
from the type t
, then for each m
find its custom attributes a
, then if the set a
contains the attribute you want, you invoke the method.
See also:
Which would look like this:
foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (Type t in a.GetTypes())
{
// Skip types that don't have the [Invoke] attribute
var typeContainsInvokeAttribute = t.GetCustomAttributes(typeof(InvokeAttribute)).Any();
if (!typeContainsInvokeAttribute)
{
continue;
}
// This throws for types without a public, parameterless constructor
var instance = Activator.CreateInstance(t);
foreach (var methodInfo in instance.GetType().GetMethods())
{
var containsInvokeAttribute = methodInfo.GetCustomAttributes(typeof(InvokeAttribute)).Any();
if (containsInvokeAttribute)
{
methodInfo.Invoke(instance);
}
}
}
}
Upvotes: 2
Reputation: 37337
It's little bit unclear to me, but I think I have a solution:
You can't do directly what you're asking, but there's workaround - create interface containing only one method: invoke
(or more, as you please), in main method, create list of objects implementing your interface - or just create as a field and use it in that method. Then, in simple foreach
loop you can call Invoke
method on every item in the list (it will be possible, since they implement interface with that method).
Upvotes: 0