2

I wrote an abstract class that uses reflection to find fields marked with an Attribute in the constructor, like this:

[AttributeUsage(AttributeTargets.Field)]
public class TrackedField : Attribute {}

public class ATrackedObject {
    public ATrackedObject() {
        Type objType = this.GetType();
        foreach(FieldInfo f in objType.GetFields()) {
            if(f.IsDefined(typeof(TrackedField)), false)) {
                //Add field to list to keep tabs on periodically
            }
        }
    }
}

What I would like to do, is:

  1. Create another attribute, TrackedMethod
  2. In the constructor, in the same fashion as with the TrackedFields, find all methods tagged with TrackedMethod
  3. Change the method so that when it gets called, a method inside ATrackedObject gets called first or instead, either by replacing the method entirely or by injecting code into it, but without breaking the ability to at least see what the original method call was, including parameters
  4. No third-party libaries, and should be .NET 3.5 compatible

I have seen a number of SE threads discussing how to do this, and all of the answers relied on being able to use a third party library - however, none of the questions seemed to match my use case exactly - I can ensure 100% that every object that needs to have methods tracked will inherit from the class that does the tracking.

I don't know for sure this is possible, but I am wondering since IoC/AOP libraries like PostSharp and others exist, surely they must operate by some kind of mechanism - I assume Reflection.Emit plays a part - but what, and how?

Here are some similar questions, which did not have what seemed to me to be answers to my specific scenario, or relied on third party libraries:

custom-attribute-to-process-info-before-a-method-is-called

how-do-i-intercept-a-method-call-in-c (this was helpful, but missing the crucial component of how to actually inject code

intercept-method-calls

Is there a way to make this technique (Attribute -> Base Class Constructor -> Reflection -> Method Interception) viable?

Vyross
  • 109
  • 1
  • 12
  • _"No third-party libraries"_ -- then your question is too broad. There are several tools available that can do this sort of thing (see "aspect-oriented programming", PostSharp, etc.). But if you rule those out, then no...the language doesn't include this and you're basically left to write the entire feature yourself. Which you can do (obviously, since other people have), but explaining how to do so is well outside the scope of a good, useful Stack Overflow question. – Peter Duniho Sep 11 '17 at 23:36
  • Tell me why on earth in late 2017 you're limited to .NET 3.5? – abatishchev Sep 11 '17 at 23:38
  • Using reflection to intercept methods is like reading a newspaper and hoping that'll rewrite the articles. – Enigmativity Sep 12 '17 at 00:46

1 Answers1

4

Could something like this work for what you need?

[AttributeUsage(AttributeTargets.Field)]
public class TrackedField : Attribute { }

public class ATrackedObject
{
    public ATrackedObject()
    {
        Type objType = this.GetType();
        foreach (FieldInfo f in objType.GetFields())
        {
            if (f.IsDefined(typeof(TrackedField), false)) {
                if (f.FieldType == typeof(Action))
                {
                    var currentValue = f.GetValue(this) as Action;
                    Action newValue = () =>
                    {
                        Console.WriteLine($"Tracking {f.Name}");
                        currentValue.Invoke();
                    };

                    f.SetValue(this, newValue);
                }
            }
        }
    }

    [TrackedField]
    public Action SomeMethod = () => Console.WriteLine("Some Method Called");
}

this is done with a simple Action property but it could be extended to use Func<>

edit: forgot to include usage and output.

usage:

static void Main(string[] args)
{
    var obj = new ATrackedObject();
    obj.SomeMethod();

    Console.Read();
}

output:

Tracking SomeMethod
Some Method Called
Aaron Roberts
  • 1,342
  • 10
  • 21
  • 1
    Wow, that was fast! If you know how to make it work with Func<> that would be very helpful. Right now, I'm having some trouble making it work with Actions if the Action has parameters - like Action or Action. The basic operation does work, though, and I can do at least some of what I wanted with this, although solving for Func<> or Action with parameters would be ideal! – Vyross Sep 12 '17 at 01:13
  • Sure, I can update the answer, however what is the functionality you are expecting to inject into these methods? There is most likely a better solution than this though this will "work" – Aaron Roberts Sep 12 '17 at 01:22
  • Each method might have different parameters and return types, although if that's impossible to work with I could deal with just "object" in and out. I'm writing a utility to help cut down on boilerplate code for a networking project, although to be honest, my stubbornness is a major component in wanting this to work as well. – Vyross Sep 12 '17 at 01:50
  • The idea is to intercept certain methods as they're called to invoke other methods - in this case the plan was to have several fields inside the attribute to determine what actions to take, including logging and triggering RPCs on network peers, but potentially also to be used in other ways. This might be a case of "being so preoccupied with whether or not you could, you didn't stop to think if you should" scenario, but I'm really curious at this point. – Vyross Sep 12 '17 at 01:57