views:

146

answers:

3

Hi.

I'm writing a system that underlies programmer applications and that needs to detect their access to certain data. I can mostly do so with properties, like this:

public class NiceClass {
    public int x { get; set; }
}

Then I go in and tweak the get and set accessors so that they handle the accesses appropriately. However this requires that the users (application programmers) define all of their data as properties.

If the users want to use pre-existing classes that have "normal" fields (as opposed to properties), I cannot detect those accesses. Example:

public class NotSoNiceClass {
    public int y;
}

I cannot detect accesses to y. However, I want to allow the use of pre-existing classes. As a compromise the users are responsible for notifying me whenever an access to that kind of data occurs. For example:

NotSoNiceClass notSoNice;
...
Write(notSoNice.y, 0);  // (as opposed to notSoNice.y = 0;)

Something like that. Believe me, I've researched this very thoroughly and even directly analysing the bytecode to detect accesses isn't reliable due to possible indirections, etc. I really do need the users to notify me.

And now my question: could you recommend an "elegant" way to perform these notifications? (Yes, I know this whole situation isn't "elegant" to begin with; I'm trying not to make it worse ;) ). How would you do it?

This is a problem for me because actually the situation is like this: I have the following class:

public class SemiNiceClass {
     public NotSoNiceClass notSoNice { get; set; }
     public int z { get; set; }
}

If the user wants to do this:

SemiNiceClass semiNice;
...
semiNice.notSoNice.y = 0;

They must instead do something like this:

semiNice.Write("notSoNice").y = 0;

Where Write will return a clone of notSoNice, which is what I wanted the set accessor to do anyway. However, using a string is pretty ugly: if later they refactor the field they'll have to go over their Write("notSoNice") accesses and change the string.

How can we identify the field? I can only think of strings, ints and enums (i.e., ints again). But:

  • We've already discussed the problem with strings.
  • Ints are a pain. They're even worse because the user needs to remember which int corresponds to which field. Refactoring is equally difficult.
  • Enums (such as NOT_SO_NICE and Z, i.e., the fields of SemiNiceClass) ease refactoring, but they require the user to write an enum per class (SemiNiceClass, etc), with a value per field of the class. It's annoying. I don't want them to hate me ;)

So why, I hear you ask, can we not do this (below)?

semiNice.Write(semiNice.notSoNice).y = 0;

Because I need to know what field is being accessed, and semiNice.notSoNice doesn't identify a field. It's the value of the field, not the field itself.

Sigh. I know this is ugly. Believe me ;)

I'll greatly appreciate suggestions.

Thanks in advance!

(Also, I couldn't come up with good tags for this question. Please let me know if you have better ideas, and I'll edit them)


EDIT #1: Hightechrider's suggestion: Expressions.

I don't know if Write(x =>semiNice.y, 0) was meant to be based on the classes I wrote for my question (SemiNiceClass, etc) or if it's just an example, but if it's the former it doesn't match the structure: there's no y field in SemiNiceClass. Did you mean Write(x =>semiNice.notSoNice.y, 0)?

I'm not sure how you mean for me to use this... I'll post the code I've written:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Test.MagicStrings {

    public class MagicStringsTest {
        public static void Main(string[] args) {
            SemiNiceClass semiNice = new SemiNiceClass();
            // The user wants to do:  semiNice.notSoNice.y = 50;
            semiNice.Write(x => semiNice.notSoNice.y, 50);
        }
    }

    public class SemiNiceClass {

        public NotSoNiceClass notSoNice { get; set; }
        public int z { get; set; }

        public SemiNiceClass() {
            notSoNice = new NotSoNiceClass();
        }

        public void Write(Func<object, object> accessor, object value) {
            // Here I'd like to know what field (y, in our example) the user wants to
            // write to.
        }

    }

    public class NotSoNiceClass {
        public int y;
    }

}

How do I get that info in Write? I can't find any way to extract that information out of the Func<,>. Also, why write semiNice.Write(x => semiNice.notSoNice.y, 50); instead of semiNice.Write(() => semiNice.notSoNice.y, 50);, since we're not using x for anything?

Thanks.


EDIT #2: Hans Passant's suggestion: replacing fields with properties.

This is what I originally intended to do, but recompiling is not an option.


EDIT #3: Ben Hoffstein's suggestion: dynamic proxies; LinFu.

I've already looked into this long and hard, and for relatively complex reasons I cannot use it. It would be too long to explain, but rest assured: if I could use it, I would. It's much, much neater than my current solution.

+2  A: 

Use an Expression, e.g. Write(x =>semiNice.y, 0)

This technique is used often as a way to avoid magic strings.

e.g.

    public void Write<T,U>(T source, Expression<Func<T, U>> lambda, U value) 
    {
        var body = lambda.Body as MemberExpression;
        string memberName = body.Member.Name;
        if (body.Member.MemberType == MemberTypes.Field)
        {
            (body.Member as FieldInfo).SetValue(source, value);
        }
        else if (body.Member.MemberType == MemberTypes.Method)
        {
            (body.Member as MethodInfo).Invoke(source, new object[]{value});
        }
        Console.WriteLine("You wrote to " + memberName + " value " + value);
    }
Hightechrider
I've updated my question. See EDIT #1. Thanks :)
Alix
And I updated answer for you.
Hightechrider
Ah, perfect. +1, and I'm marking this answer and Akash's as the answers. Thanks a lot :)
Alix
Oops. I can only mark one as the answer. Yours it is, since you were the first one to suggest Expressions.
Alix
+1  A: 

Have you considered using a dynamic proxy to intercept all calls to the target classes, do whatever it is you need to do, and then forward the call to the target?

Something like LinFu may do the trick: http://www.codeproject.com/KB/cs/LinFuPart1.aspx

Ben Hoffstein
I've looked a lot into LinFu and other AOP frameworks, as well as very powerful stuff such as the Profiling API. I really wanted to use this, but for reasons that would take very long to explain I cannot use them. Thanks for the suggestion though :)
Alix
+1  A: 

Following code suports Refractoring, No additional code to write, however it may not execute very fast because of reflection but, it certainly will give you access to what you want.

You have to play with Expression tree a little as follow,

public class MagicStringsTest
{
    public static void Main(string[] args)
    {
        SemiNiceClass semiNice = new SemiNiceClass();
        // The user wants to do:  semiNice.notSoNice.y = 50;

        semiNice.Write( t=>t.y , 50);

        Console.ReadLine();
    }
}

public class SemiNiceClass
{

    public NotSoNiceClass notSoNice { get; set; }
    public int z { get; set; }

    public SemiNiceClass()
    {
        notSoNice = new NotSoNiceClass();
    }

    public void Write<R>(Expression<Func<NotSoNiceClass,R>> exp, R value)
    {
        if (exp.Body.NodeType == ExpressionType.MemberAccess)
        {
            MemberExpression e = exp.Body as MemberExpression;
            Console.WriteLine("Writing value for " + e.Member.Name 
                + " of NotSoNiceClass");
            FieldInfo info = e.Member as FieldInfo;

            // value is set using reflection...
            info.SetValue(notSoNice, value);
        }
        else
        {
            // throw exception, expecting of type x=>x.y
        }
    }


}
Akash Kava
Thanks :)I gave you +1 but I can only mark 1 answer as *the* answer and I've chosen Hightechrider's because he was the one to suggest Expressions in the first place. I wish I could mark both :/
Alix
Its ok, thank you.
Akash Kava