开发者

Class member as a first-class object

I was wondering if there is something in c# to be able to pass a member of a class to another function that will use this member to get a value. So get a value of a field determined only which one at runtime. Something like in other languages (PHP at least I think) that you can do

a.b = "something"

but also

a["b"] = "something";

edit: actually not so good an example since a string is used, sorry

For clarity an example of what I'd like to be able to do:

class A
{
    int x;
    int y;
}

void somethingsomething<T>(T class, SomeMagicFieldClass f)
{
    dosomethingwith(somemethodthatgivesmethevalueoffield(class, f));
}

Where then I can call the method like this:

A a = new A();
somethingsomething(a, A.x); //hypothetical notation
somethingsomething(a, A.y);

I now have something similar where I do:

somethingsomething(a, "x");
somethingsomething(a, "y");

I then go find the field using introspection API (also trying GetProperty)

MemberInfo memberInfo = item.GetType().GetField(fieldName);

This works but the disadvantage 开发者_C百科is that the fields passed as a string won't get updated when "refactoring" fieldnames in visual studio, so I was thinking maybe there exists something like this in c# that would get refactored automatically when changing field names?

Thanks a lot for reading this boring question


Your example looks a lot like a LINQ key selector, in that form it would look like:

A a = new A();
somethingsomething(a, p => p.x);


You can do some nice refactor-friendly things with LINQ Expressions. Here is a snippet of utilty code I used for such occasions. It allows you to get the Name, Type and Value of a property (it won't work with fields without modifications). There's also a setter for the value.

public static void Main(string[] args) {
    var test = new { Test1 = 42, Test2 = "123", Test3 = 3.14195 };

    somethingSomething(test, t => t.Test1);
    somethingSomething(test, t => t.Test2);
    somethingSomething(test, t => t.Test3);
}

static void somethingSomething<TObj,TProperty>(TObj obj, Expression<Func<TObj,TProperty>> expr) {
    var accessor = GetMemberAccessor(expr, obj);

    String name = accessor.Name;
    TProperty value = accessor.Value;
    String typeName = accessor.Type.Name;
    Console.WriteLine("{0} = {1} ({2})", name, value, typeName);
}

The output of that would be:

Test1 = 42 (Int32)
Test2 = 123 (String)
Test3 = 3.14195 (Double)

To make this work, I used the following helper function and class:

public static MemberAccessor<TReturn> GetMemberAccessor<TObj,TReturn>(Expression<Func<TObj, TReturn>> expr, TObj tar) {
    var body = expr.Body;

    MemberExpression memberExpression = null;
    if (body is UnaryExpression) {
        var ue = (UnaryExpression)body;
        memberExpression = (MemberExpression)ue.Operand;
    } else if (body is MemberExpression)
        memberExpression = (MemberExpression)body;
    else
        throw new NotImplementedException("can't get MemberExpression");

    String name = memberExpression.Member.Name;

    return new MemberAccessor<TReturn>(tar, name);
}

public class MemberAccessor<T> {
    private readonly PropertyDescriptor propertyDesc;
    private readonly Object target;

    public MemberAccessor(Object target, String propertyName) {
        this.target = target;
        this.propertyDesc = TypeDescriptor.GetProperties(target)[propertyName];
    }
    public String Name {
        get { return propertyDesc.Name; }
    }
    public Type Type {
        get { return propertyDesc.PropertyType; }
    }        
    public T Value {
        get { return (T)Convert.ChangeType(propertyDesc.GetValue(target), typeof(T)); }
        set { propertyDesc.SetValue(target, value); }
    }
}


Mr. Plunkett is correct; a dynamic type will do the job. Luckily, the .NET 4 team included a handy object called the ExpandoObject that solves that for you.


You asked how to

pass a member of a class to another function that will use this member to get a value

You can usedelegates for this

   class A
   {
       public string aField;

       public string aProperty{get{return "someval";}}

       public string aMemberFunction(){return "someval";}
   }


   void get_a_value(Func<string> func)
   {
       string theValue = func();
   }

   // use it:
   A a = new A();

   get_a_value( () => a.aField);
   get_a_value( () => a.aProperty);
   get_a_value( () => a.aMemberFunction());

What you don't get this way, of course, is a separation of parameters for the memberfunction and the object you are passing.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜