I have a class with quite a lot of properties and I need to post this class property names to a webservice.
Easy solution is just to create that array by hand, as in new[] {"Id", "Name", "Date", "etc"}.
But that's not fun, I would like to have intellisense support. So far I came up with creating an enum with all these properties, then having a helper function which takes array of these enums and calls .ToString() on each and adds to array.
Problem - quite an useless enum and if my class gets updated, I would need to manualy sync that enum with class properties.
Ideal solution in my mind would be to have something like LINQ extension method, where I could pass properties, something like with Select - ToPropertiesArray(x => {x.Id, X.Name, x.Date})
Am I just crazy and this cannot be done and is just plainly stupid? Or a suggestion on how to pass property names with some kind of IntelliSense support?
public class MyClass
{
public int Id{get;set;}
public string S{get;set;}
public double D{get;set;}
}
public static string[] GetPropsNamesArray<T>(Expression<Func<T,Object>> expr)
{
var t = GetObjectType(expr);
var res = t.GetProperties(BindingFlags.Instance|BindingFlags.Public)
.Select(pi => pi.Name)
.ToArray();
return res;
}
public static Type GetObjectType<T>(Expression<Func<T, object>> expr)
{
if ((expr.Body.NodeType == ExpressionType.Convert) ||
(expr.Body.NodeType == ExpressionType.ConvertChecked))
{
var unary = expr.Body as UnaryExpression;
if (unary != null)
return unary.Operand.Type;
}
return expr.Body.Type;
}
and use:
var selectedPropsNames = GetPropsNamesArray<MyClass>(m => new {m.Id,m.S});
var allPropsNames = GetPropsNamesArray<MyClass>(m => m);
As Lars said, you can use reflection. Using reflection in a method also gets you not having to rewrite when the properties collection changes. The beginning of a sample below iterates the public properties of an entity.
System.Reflection.PropertyInfo[] properties = entity.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
foreach (System.Reflection.PropertyInfo propertyInfo in properties)
{
// ...
}
To have Intellisense support, you can use Expressions:
public static class Helper
{
public static List<string> ToPropertiesArray(params System.Linq.Expressions.Expression<Func<object>>[] exprs)
{
return exprs.Select(expr => ((expr.Body as System.Linq.Expressions.UnaryExpression).Operand
as System.Linq.Expressions.MemberExpression).Member.Name)
.ToList();
}
}
with sample usage:
SomeClass cl = new SomeClass();
var types = Helper.ToPropertiesArray(() => cl.SomeField, () => cl.SomeOtherField);
Related
Many of my classes have a DisplayName property using string interpolation, for example:
DisplayName = $"{CutoutKind} {EdgeKind} {MaterialKind}";
where each element in {} is a class Property Name.
What I would like to do is to retrieve the String being interpolated from the database, something like
displayName = SomeFunction(StringFromDatabase, this);
Where StringFromDatabase is a variable, value set from the database, = "{CutoutKind} {EdgeKind} {MaterialKind}"
However I want to do this without using reflection
Is there some different way of achieving what I want?
Doing this at runtime without using reflection would mean that a generic solution is not possible. You would have to write different method for each class you want to support. A very simple version:
static string SomeFunction(string format, MyClass instance)
{
return format.Replace("{CutoutKind}", instance.CutoutKind.ToString())
.Replace("{EdgeKind}", instance.EdgeKind.ToString())
.Replace("{EdgeKind}", instance.MaterialKind.ToString());
}
Or a slightly more sophisticated version:
Dictionary<string, Func<MyClass, string>> propertyGetters =
new Dictionary<string, Func<MyClass, string>>
{
{ "CutoutKind", x => x.CutoutKind.ToString() }
{ "EdgeKind", x => x.EdgeKind.ToString() }
{ "EdgeKind", x => x.MaterialKind.ToString() }
};
static string SomeFunction(string format, MyClass instance)
{
return Regex.Replace(#"\{(\w+)\}",
m => propertyGetters.HasKey(m.Groups[1].Value)
? propertyGetters[m.Groups[1].Value](instance)
: m.Value;
}
But if you decide you don't want to have to write this kind of method for each class, here's a simple generic version using reflection:
static string SomeFunction<T>(string format, T instance)
{
var propertyInfos = typeof(T)
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.ToDictionary(p => p.Name);
return Regex.Replace(#"\{(\w+)\}",
m => propertyInfos.HasKey(m.Groups[1].Value)
? propertyInfos[m.Groups[1].Value].GetValue(instance, null)
: m.Value;
}
Runtime Mapping objects of type A into a similar type A`
List of types is discovered run-time using System.Reflection
An expression Expression<Func<object, object>> is made for each appropriate Type. The function converts object of type oldType into an object of newType.
Sidenote:
The generic type arguments for the Func<object, object> are somewhat-misleading, it feels like it should be Func<newType,oldType>, but the types are not known compile-time:
Expression<Func<object, object>> GetConvertExpression(Type oldType, Type newType)
The expressions are compiled and stored into a Dictionary<Type, Func<object, object>>
Question - How to solve the downsides of the current approach?
A dictionary lookup is necessary to convert an object. I had thought of baking in a big if/then/else or case statment into a big unified Expression, this could work well with a small number of types.
Unnecessary casting operations within the generated IL.
Argument and return type of the generated code is of type object. This basically ensures that there will be casting operations all around. Especially when a large transformation of List<oldType> to List<newType> has to be performed.
Generated code from the Expression:
object convert(object oldInst)
{
newType newInst = newType();
oldType oldInstCast = (oldType)oldInst;
newInst.field1 = oldInstCast.field1;
.......
// note: if some field of oldInst has a type in the DICTIONARY and
// it's value is not null, we have to perform the convert
// operation recursively for that field
if(oldInstCast.field3 != null)
{
// Type of the field was determined when this code was generated
// And it is known that the dictionary will contain it
Func<object, object> transformFun = dictionary[oldFieldType];
newInst.field3 = (newFieldType)transformFun(oldInstCast.field3);
}
newInst.fieldz = oldInstCast.fieldz;
return newInst;
}
EDIT: I wrote the original answer at around midnight. I think it shows. It sounds like what you need most are how to construct the delegate when the parameter and return types are only known at runtime. You get around this problem by using object. You can still do it strongly typed. The most relevant calls are:
Type delegateType = typeof(Func<,>).MakeGenericType(typeof(A), typeof(NewA));
This creates the delegate type Func<A, NewA> as for converting from A to NewA. To create an Expression<Func<A, NewA>> when A and NewA aren't necessarily known at compile time, you can use this overload of Expression.Lambda.
Previous answer:
Excellent question. I had fun trying to draw up a solution with stronger typing.
I did a mockup of this and I see no reason you can't generate expressions of type Expression<Func<TOld, TNew>> instead of Expression<Func<object, object>>. You wouldn't need casting inside of those conversion functions. However, I'm still not sure it's a better solution. Here's what I came up with so far:
The meat of the conversion/reflection/expression code is in a class I called ConverterDictionary, which builds these converters then holds them in a dictionary, and offers them up in a nicely typed way. I'm guessing that 95% of this is non-material for you since it sounds like you already figured out the reflection and expression-building portions of your problem. Everything else is 100% sample code.
Let me know what needs clarification or further explanation, or what could be more helpful.
class Program
{
static void Main(string[] args)
{
var cv = new ConverterDictionary();
var type2_converter = cv.GetConverter<Type2, Type2_New>();
var converterF = type2_converter.Compile();
var type2 = new Type2 { Field1 = "Hello", Field2 = "World" };
var type2_New = converterF(type2);
}
}
public class ConverterDictionary
{
private Dictionary<Tuple<Type, Type>, object> conversionDict = new Dictionary<Tuple<Type, Type>, object>();
public ConverterDictionary()
{
var convertPairs = Assembly.GetExecutingAssembly().GetTypes()
.Where(t => t.CustomAttributes.Any(a => a.AttributeType == typeof(ClassConvertAttribute)))
.Select(t => Tuple.Create(t, (Type)(t.CustomAttributes.First().NamedArguments[0].TypedValue.Value)))
.ToList();
foreach(var pair in convertPairs)
{
var fromType = pair.Item1;
var toType = pair.Item2;
var fieldConversions = fromType.GetFields()
.Where(f => f.CustomAttributes.Any(a => a.AttributeType == typeof(FieldConvertAttribute)))
.Select(f => Tuple.Create(f, toType.GetField((string)(f.CustomAttributes.First().NamedArguments[0].TypedValue.Value))))
.ToList();
var delegateType = typeof(Func<,>).MakeGenericType(fromType, toType);
var param1 = Expression.Parameter(fromType, "oldInst");
var returnVar = Expression.Variable(toType, "newInst");
var expr = Expression.Lambda(
delegateType,
Expression.Block(
new ParameterExpression[] { returnVar },
new Expression[]
{
Expression.Assign(
returnVar,
Expression.New(toType)
),
}.Concat(
fieldConversions.Select(fc =>
Expression.Assign(
Expression.MakeMemberAccess(
returnVar,
fc.Item2
),
Expression.MakeMemberAccess(
param1,
fc.Item1
)
)
)
).Concat(
new Expression[] { returnVar }
)
),
param1
);
conversionDict[pair] = expr;
}
}
public Expression<Func<TFrom, TTo>> GetConverter<TFrom, TTo>()
{
var key = Tuple.Create(typeof(TFrom), typeof(TTo));
if (conversionDict.ContainsKey(key))
return conversionDict[key] as Expression<Func<TFrom, TTo>>;
return null;
}
}
[ClassConvert(ToType=typeof(Type1_New))]
public class Type1
{
[FieldConvert(ToFieldName = "Field1")]
public string Field1;
}
[ClassConvert(ToType = typeof(Type2_New))]
public class Type2
{
[FieldConvert(ToFieldName="Field1")]
public string Field1;
[FieldConvert(ToFieldName = "Field2_New")]
public string Field2;
}
public class Type1_New
{
public string Field1;
}
public class Type2_New
{
public string Field1;
public string Field2_New;
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
public class ClassConvertAttribute : Attribute
{
public Type ToType { get; set; }
}
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public class FieldConvertAttribute : Attribute
{
public string ToFieldName { get; set; }
}
Can I access object properties as a type?
I am using an API where I have to iterate through a collection of objects, and access the Text properties of two of these objects, either for reading or for writing. I currently have two methods for reading and writing as follows:
Result ReadTexts()
var attribs = SOME_CODE;
string first = "", second = "";
for(int i=1 ; i <= attribs.Count ; i++) {
if(attribs[i] IS_FIRST_ONE_NEEDED) {
first = attribs[i].Text;
} else if(attribs[i] IS_SECOND_ONE_NEEDED) {
second = attribs[i].Text;
}
}
return new Result(first, second);
}
void WriteTexts(string first, string second) {
var attribs = SOME_CODE;
for(int i=1 ; i <= attribs.Count ; i++) {
if(attribs[i] IS_FIRST_ONE_NEEDED) {
attribs[i].Text = first;
} else if(attribs[i] IS_SECOND_ONE_NEEDED) {
attribs[i].Text = second;
}
}
}
What I would prefer is using a more functional style which factors out the iteration and checking for the two objects in the collection into one method instead of repeating this code, as actually SOME_CODE as well as IS_FIRST_ONE_NEEDED and IS_SECOND_ONE_NEEDED are a bit longer in reality than in the above sample code. This one method would look like:
void AccessTexts(Action<StringProperty> first, Action<StringProperty> second) {
var attribs = SOME_CODE;
for(int i=1 ; i <= attribs.Count ; i++) {
if(attribs[i] IS_FIRST_ONE_NEEDED) {
first(attribs[i].Text);
} else if(attribs[i] IS_SECOND_ONE_NEEDED) {
second(attribs[i].Text);
}
}
}
and then call this with lambda expressions like
AccessTexts(( prop => prop = "abc"), ( prop => prop = "def"));
for writing, or
AccessTexts(( prop => firstString = prop), ( prop => secondString = prop));
for reading. This would be much shorter and avoid repeating a lot of code.
But I think this is not possible, as properties are not exposed as a real type in .net, but are just based on the availability of special methods - the getter and setter. Hence, there is no type StringProperty as I used it as type of the delegate parameter in the code sample of "what I would like to write".
Am I right, or is there some way to implement it the way I want?
You can create your own class that represents a property. As you've shown, a property is essentially just a get and set method, so that's all our class needs to represent.
As for how to create such a thing, one option is to have the type accept a getter and setter as delegates directly. Another option is to have it accept a PropertyInfo and an object which can then use reflection to implement the getter and setter methods. Finally, if you wanted to, you could even use an Expression to represent the property access, and then pull out the PropertyInfo from that.
So to start out with, the actual wrapper itself:
public class PropertyWrapper<T>
{
private Func<T> getter;
private Action<T> setter;
public PropertyWrapper(PropertyInfo property, object instance)
{
if (!typeof(T).IsAssignableFrom(property.PropertyType))
throw new ArgumentException("Property type doesn't match type supplied");
setter = value => property.SetValue(instance, value);
getter = () => (T)property.GetValue(instance);
}
public PropertyWrapper(Func<T> getter, Action<T> setter)
{
this.setter = setter;
this.getter = getter;
}
public T Get()
{
return getter();
}
public void Set(T value)
{
setter(value);
}
public T Value
{
get { return getter(); }
set { setter(value); }
}
}
You can then use a helper such as this (it's extracted out of the other class so that there is generic type inference:
public class PropertyWrapper
{
public static PropertyWrapper<TProp> Create<TObject, TProp>(
TObject instance, Expression<Func<TObject, TProp>> expression)
{
var memberEx = expression.Body as MemberExpression;
var prop = memberEx.Member as PropertyInfo;
return new PropertyWrapper<TProp>(prop, instance);
}
}
Here is a simple example of constructing such an object using the two different syntaxes:
var list = new List<int>();
var prop1 = new PropertyWrapper<int>(
() => list.Capacity, cap => list.Capacity = cap);
var prop2 = PropertyWrapper.Create(list, l => l.Capacity);
prop2.Value = 42;
Console.WriteLine(list.Capacity); //prints 42
What you did is definitely workable. You are providing an accessor for the property in question to your function AccessTexts so that the function does not care how the access is done.
Normally, this would be solved using an interface implemented by the object being iterated over, or implemented by a wrapper class.
You can also use reflection or dynamic to do the access.
In any case you need a proxy between AccessTexts and the real objects.
I have a class MyDummyClass to which I'd like to pass some properties in form of a Lambda expression for a later evaluation. So what I can do something like
public class MyDummyClass<T>
{
public MyDummyClass(Expression<Func<T, object>> property)
{
...
}
...
}
..and then use that class like new MyDummyClass<Person>(x=>x.Name), right?
But then I'd like to pass not only a single property but a list of properties. So I'd write my class like
public class MyDummyClass<T>
{
public MyDummyClass(IEnumerable<Expression<Func<T, object>>> properties)
{
...
}
...
}
and I'd like to use it like new MyDummyClass<Person>(new[] { x=>x.Name, x=>x.Surname }) but unfortunately that doesn't work! Instead I have to write
new MyDummyClass<Person>
(new Expression<Func<Person, object>>[] { x=>x.Name, x=>x.Surname});
But this is a bit awkward to write, isn't it? Of course, using params would work, but this is just a sample out of a more complicated piece of code where using params is not an option.
Does anyone have a better option to come out of this??
Try using params instead:
public MyDummyClass(params Expression<Func<T, object>>[] properties)
Then you should be able to do:
var dummy = new DummyClass<Person>(x => x.Name, x => x.Surname);
You could try:
public class MyDummyClass<T>
{
public MyDummyClass(Expression<Func<T, object>> expression)
{
NewArrayExpression array = expression.Body as NewArrayExpression;
foreach( object obj in ( IEnumerable<object> )( array.Expressions ) )
{
Debug.Write( obj.ToString() );
}
}
}
And then you would call it like this:
MyDummyClass<Person> cls = new MyDummyClass<Person>( item => new[] { item.Name, item.Surname } );
The problem is this won't give you the value of the property because no actual Person instance it specified Doing a ToString on "obj" will give you the name of the property. I don't know if this is what you're after, but it maybe a starting point.
I know there are a few answers on the site on this and i apologize if this is in any way duplicate, but all of the ones I found does not do what I am trying to do.
I am trying to specify method info so I can get the name in a type safe way by not using strings.
So I am trying to extract it with an expression.
Say I want to get the name of a method in this interface:
public interface IMyInteface
{
void DoSomething(string param1, string param2);
}
Currently I can get the name using THIS method:
MemberInfo GetMethodInfo<T>(Expression<Action<T>> expression)
{
return ((MethodCallExpression)expression.Body).Method;
}
I can call the helper method as follows:
var methodInfo = GetMethodInfo<IMyInteface>(x => x.DoSomething(null, null));
Console.WriteLine(methodInfo.Name);
But I am looking for the version that I can get the method name without specifying the parameters (null, null)
like this:
var methodInfo = GetMethodInfo<IMyInteface>(x => x.DoSomething);
But all attempts fail to compile
Is there a way to do this?
x => x.DoSomething
In order to make this compilable I see only two ways:
Go non-generic way and specify it's parameter as Action<string, string>
Specify Action<string, string> as your target delegate type by yourself: GetMethodInfo<IMyInteface>(x => new Action<string,string>(x.DoSomething))
if you are ok to go with second one, which allows you to omit arguments then you can write your GetMethodInfo method as follows:
MemberInfo GetMethodInfo<T>(Expression<Func<T, Delegate>> expression)
{
var unaryExpression = (UnaryExpression) expression.Body;
var methodCallExpression = (MethodCallExpression) unaryExpression.Operand;
var methodInfoExpression = (ConstantExpression) methodCallExpression.Arguments.Last();
var methodInfo = (MemberInfo) methodInfoExpression.Value;
return methodInfo;
}
It works for your interface, but probably some generalization will be required to make this working with any method, that's up to you.
The following is compatible with .NET 4.5:
public static string MethodName(LambdaExpression expression)
{
var unaryExpression = (UnaryExpression)expression.Body;
var methodCallExpression = (MethodCallExpression)unaryExpression.Operand;
var methodCallObject = (ConstantExpression)methodCallExpression.Object;
var methodInfo = (MethodInfo)methodCallObject.Value;
return methodInfo.Name;
}
You can use it with expressions like x => x.DoSomething, however it would require some wrapping into generic methods for different types of methods.
Here is a backwards-compatible version:
private static bool IsNET45 = Type.GetType("System.Reflection.ReflectionContext", false) != null;
public static string MethodName(LambdaExpression expression)
{
var unaryExpression = (UnaryExpression)expression.Body;
var methodCallExpression = (MethodCallExpression)unaryExpression.Operand;
if (IsNET45)
{
var methodCallObject = (ConstantExpression)methodCallExpression.Object;
var methodInfo = (MethodInfo)methodCallObject.Value;
return methodInfo.Name;
}
else
{
var methodInfoExpression = (ConstantExpression)methodCallExpression.Arguments.Last();
var methodInfo = (MemberInfo)methodInfoExpression.Value;
return methodInfo.Name;
}
}
Check this sample code on Ideone.
Note, that Ideone does not have .NET 4.5.
The problem with this is that x.DoSomething represents a method group. And you have to somehow explicitly specify what delegate type do you want to convert that method group into, so that the correct member of the group can be selected. And it doesn't matter if that group contains only one member.
The compiler could infer that you mean that one, but it doesn't do that. (I think it's this way so that your code won't break if you add another overload of that method.)
Snowbear's answer contains good advice on possible solutions.
This is a new answer to an old question, but responds to the "verbose" complaint of the accepted answer. It requires more code, but the result is a syntax like:
MemberInfo info = GetActionInfo<IMyInterface, string, string>(x => x.DoSomething);
or, for methods with a return value
MemberInfo info = GetFuncInfo<IMyInterface, object, string, string>(x => x.DoSomethingWithReturn);
where
object DoSomethingWithReturn(string param1, string param2);
Just like the framework provides Action<> and Func<> delegates up to 16 parameters, you have to have GetActionInfo and GetFuncInfo methods that accept up to 16 parameters (or more, although I'd think refactoring is wise if you have methods with 16 parameters). A lot more code, but an improvement in the syntax.
If you are ok with using the nameof() operator you can use the following approach.
One of the benefits is not having to unwrap an expression tree or supply default values or worry about having a non-null instance of the type with the method.
// As extension method
public static string GetMethodName<T>(this T instance, Func<T, string> nameofMethod) where T : class
{
return nameofMethod(instance);
}
// As static method
public static string GetMethodName<T>(Func<T, string> nameofMethod) where T : class
{
return nameofMethod(default);
}
Usage:
public class Car
{
public void Drive() { }
}
var car = new Car();
string methodName1 = car.GetMethodName(c => nameof(c.Drive));
var nullCar = new Car();
string methodName2 = nullCar.GetMethodName(c => nameof(c.Drive));
string methodName3 = GetMethodName<Car>(c => nameof(c.Drive));
If your application would allow a dependency on Moq (or a similar library), you could do something like this:
class Program
{
static void Main(string[] args)
{
var methodName = GetMethodName<IMyInteface>(x => new Action<string,string>(x.DoSomething));
Console.WriteLine(methodName);
}
static string GetMethodName<T>(Func<T, Delegate> func) where T : class
{
// http://code.google.com/p/moq/
var moq = new Mock<T>();
var del = func.Invoke(moq.Object);
return del.Method.Name;
}
}
public interface IMyInteface
{
void DoSomething(string param1, string param2);
}