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.
精彩评论