C#: How to Create an Attribute on a Method Triggering an Event When It Is Invoked

C#: How to create an attribute on a method triggering an event when it is invoked?

The only way I know how to do this is with PostSharp. It post-processes your IL and can do things like what you asked for.

Using C# Attributes, how would I trigger functions to be executed on change of their assigned field

I believe, for your case, you could go one of two routes. A broadcast style system, or a registration style system.

In a Broadcast scenario, your communications component (class), could declare something like:

public event Action<(string shatNetIdentity, MessageType messageType, string message)> Message;

And then invoke when a message is correctly received:

Message?.Invoke((id, messageType, message));

The receiving object would then have to determine if the message was intended for it, and if so, what type of message it actually was and how to deal with it.

In a Register scenario, your objects could register to the communications component. You could have a Dictionary in your comms component:

private Dicitionary<string, INetworkObject> Registrations;

public void Register(INetworkObject o)
{
Registrations[o.ShatNetIdentity] = o;
}

private void ReceiveMessage()
{
// receive and parse message.
if ( Registrations.TryGet(message.Id, out INetworkObject o )
{
switch (message.messageType)
{
case MessageType.Health:
o.Health = message.Health;
break;
// etc..
}
}
}

Where INetworkObject is something like:

public interface INetworkObject
{
string ShatNetIdentity {get; set;}
float Health { get; set; }
Vector3 Position {get; set;}
}

There's obviously no error checking here. And if it were me, I'd probably go with the second scenario. The first scenario might be easier to implement if there were only a few objects listening to the event, but isn't optimal if there are many objects, as the event is broadcast to every object listening.

Trigger a method before other method execution

There is no built in way to achieve this result, if you are using a dependency injection mechanism you can use the interception facilities if the DI framework supports this. (Ex: Unity, NInject)

If you want to go low level you can also use Reflection.Emit to create a derived class at runtime, that overrides methods with a particular attribute that invokes any extra functionality you want, but that is more difficult.

How to call methods with method attributes?

To call a method, you need to instantiate a class. To instantiate a class, you need to know the type.

So we need to

  1. find all classes, that contain methods marked with the Invoke attribute
  2. Then instantiate those classes
  3. Call all marked methods.

Let'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

  1. selects all types and filters all classes
  2. it then reads all methods defined in those classes
  3. and checks that those methods are marked with the InvokeAttribute

This gives us a list of MethodInfos. 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.

How to trigger event when a variable's value is changed?

Seems to me like you want to create a property.

public int MyProperty
{
get { return _myProperty; }
set
{
_myProperty = value;
if (_myProperty == 1)
{
// DO SOMETHING HERE
}
}
}

private int _myProperty;

This allows you to run some code any time the property value changes. You could raise an event here, if you wanted.

Can I trigger an event when a string changes from another class?

You need to implement INotifyPropertyChanged interface and then call a method to emit that event when it happens, super manual unfortunately

    class MainClass : System.ComponentModel.INotifyPropertyChanged
{
private string _employee;

public event PropertyChangedEventHandler PropertyChanged;

public string Employee
{
get => _employee;
set
{
_employee = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Employee)));
}
}
}

class SecondClass
{
public SecondClass(MainClass mainClass)
{
mainClass.PropertyChanged += StringChangedEvent;
}

private void StringChangedEvent(object sender, PropertyChangedEventArgs args)
{
if(args.PropertyName == "Employee")
{
//Stuff happen if ...
}
}
}

Raise event when any of class method called

You should use AOP to do that. A simple approach would be to inherit from ContextBoundObject, check this article for detailed information.



Related Topics



Leave a reply



Submit