I want to get the PropertyInfo for a specific property. I could use:
foreach(PropertyInfo p in typeof(MyObject).GetProperties())
{
if ( p.Name == "MyProperty") { return p }
}
But there must be a way to do something similar to
typeof(MyProperty) as PropertyInfo
Is there? Or am I stuck doing a type-unsafe string comparison?
Cheers.
There is a .NET 3.5 way with lambdas/Expression that doesn't use strings...
using System;
using System.Linq.Expressions;
using System.Reflection;
class Foo
{
public string Bar { get; set; }
}
static class Program
{
static void Main()
{
PropertyInfo prop = PropertyHelper<Foo>.GetProperty(x => x.Bar);
}
}
public static class PropertyHelper<T>
{
public static PropertyInfo GetProperty<TValue>(
Expression<Func<T, TValue>> selector)
{
Expression body = selector;
if (body is LambdaExpression)
{
body = ((LambdaExpression)body).Body;
}
switch (body.NodeType)
{
case ExpressionType.MemberAccess:
return (PropertyInfo)((MemberExpression)body).Member;
default:
throw new InvalidOperationException();
}
}
}
You can use the new nameof() operator that is part of C# 6 and available in Visual Studio 2015. More info here.
For your example you would use:
PropertyInfo result = typeof(MyObject).GetProperty(nameof(MyObject.MyProperty)) ?? throw new Exception();
The compiler will convert nameof(MyObject.MyProperty) to the string "MyProperty" but you gain the benefit of being able to refactor the property name without having to remember to change the string because Visual Studio, ReSharper, and the like know how to refactor nameof() values.
You can do this:
typeof(MyObject).GetProperty("MyProperty")
However, since C# doesn't have a "symbol" type, there's nothing that will help you avoid using string. Why do you call this type-unsafe, by the way?
This is probably the best way:
public static class TypeExtensions
{
public static PropertyInfo? GetProperty<T, TValue>(this T type, Expression<Func<T, TValue>> selector) where T : class
{
Expression expression = selector.Body;
return expression.NodeType == ExpressionType.MemberAccess ? (PropertyInfo)((MemberExpression)expression).Member : null;
}
}
Usage:
myObject.GetProperty(opt => opt.PropertyName);
Reflection is used for runtime type evaluation. So your string constants cannot be verified at compile time.
Related
How would I get the JSON PropertyName of the following class & Property? Something like a "nameof()" equivilent for JSON Properties?
ie, something like
var jsonName = GetJSONPropertyName(SampleClass.SampleClassID); //should return "jsoniD"
public class SampleClass
{
public SampleClass() { }
[JsonProperty(PropertyName = "jsoniD")]
public string SampleClassID { get; set; }
}
A good question would be, how do you pass a property in a type-safe way. Properties are not first-class objects in .NET.
One of the ways would be this:
using System.Linq.Expressions;
// ...
static string GetJsonPropertyName<TC, TP>(Expression<Func<TC, TP>> expr)
{
if (expr.Body is MemberExpression body)
return body.Member.GetCustomAttribute<JsonPropertyAttribute>()?.PropertyName;
else
throw new ArgumentException("expect field access lambda");
}
You'll need to call the function like this:
var jsonName = GetJsonPropertyName<SampleClass, string>(x => x.SampleClassID);
Yes, it doesn't feel very natural. Sorry for that.
Thanks to #elgonzo, the code can be simplified like this:
static string GetJsonPropertyName<TC>(Expression<Func<TC, object>> expr)
{
// in case the property type is a value type, the expression contains
// an outer Convert, so we need to remove it
var body = (expr.Body is UnaryExpression unary) ? unary.Operand : expr.Body;
if (body is System.Linq.Expressions.MemberExpression memberEx)
return memberEx.Member.GetCustomAttribute<JsonPropertyAttribute>()?.PropertyName;
else
throw new ArgumentException("expect field access lambda");
}
var jsonName = GetJsonPropertyName<SampleClass>(x => x.SampleClassID);
Working Value-Type Expression support based on #Vlad's solution
(with UnaryExpression pattern lifted from this SO POST)
public static string GetJsonPropertyName<T>(Expression<Func<T, object>> expr)
{
if (((expr.Body as UnaryExpression)?.Operand ?? expr.Body) is MemberExpression body)
return body.Member.GetCustomAttribute<JsonPropertyAttribute>()?.PropertyName;
else
throw new ArgumentException("expect field access lambda");
}
I'm building Linq Extension methods.
Shortly, I've built an extension method in order to create a MemberExpression looks like:
public static Expression Field<T>(this object entity, string field)
{
Type entityType = entity.GetType();
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null)
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
ParameterExpression parameterExpression = Expression.Parameter(entityType, "e");
return Expression.Property(parameterExpression, propertyInfo);
}
So, I'm able to do that:
IEnumerable<C> classes = this.backend.cs.Where(
c => c.Field<C>("Matter").EndsWith(string.Empty)<<<<<<<< Compilation error.
);
Since MemberExpression have not EndsWith method, I'm not able to extend this MemberExpression like a String property access like:
IEnumerable<C> classes = this.backend.cs.Where(
c => c.Matter.EndsWith(string.Empty)
);
Is there some way to do that.
As you are able to figure out I'm trying to get something a bit more complex, Nevertheless, this example is for explaining the situation.
I hope it's enought clear.
Scope
My UI is using a backend.
This backend have three implementations. Each one of them provides a Linq implementation (Linq collections, NHibernate, custom-made Linq provider).
So, my UI is able to work on collections, a database or getting data from our server.
I'd like to provide util extension methods like AnyField().
So, after digging a bit I'm thinking on two approaches:
AnyField() generates an expression tree which is able to be translated by every Linq provider (first answer of this post).
Provide a default implementation of Anyfield() for Linq Collections, and then use each Linq provider extension mechanism for handle it. Or, if you are building a Linq Provider, support it on implementation.
Okay, so you're getting tripped up on the syntactic sugar that C# provides for you when building ExpressionTrees
Where expects Expression<Func<TObjectType, TReturnType>> or a compiled lambda; Func<TObjectType, TReturnType>.
Your method Field currently only returns an untyped Expression. That means your query is actually returning Expression<Func<TObjectType, Expression>>. That's not right! It should be returning a Expression<Func<TObjectType, string>>! But how do we do that? That would mean our method would have to return a string, but we want to build an expression tree.
To get it working as you're expecting, it's quite a bit more difficult than you would imagine, but that's only because we're so spoiled with the syntactic sugar.
What we actually need to do is write methods which accept lambda methods, and return lambda methods, each one re-writing the body a little bit.
So... what does that look like?
public static Expression<Func<TElementType, object>> Field<TElementType, TReturnType>(this Expression<Func<TElementType, TReturnType>> expr, string field)
{
Type entityType = expr.Body.Type;
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null)
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
ParameterExpression parameterExpression = Expression.Parameter(entityType, "e");
return Expression.Lambda<Func<TElementType, object>>(
Expression.Property(parameterExpression, propertyInfo),
parameterExpression
);
}
Notice that it's almost the exact same as what you wrote, but we wrap it with Lambda<Func<TElementType, TReturnType>>. And the signature is a bit different too.
Instead of operating on an object, we want to operate on a lambda expression. We also return a lambda expression.
So how do we use it?
var classes = objects.Where(
ExpressionExtensions.Field<Test, Test>(q => q, "Matter")
);
Great! Now we're passing Expression<Func<Test, string>> to Where, rather than Expression<Func<Test, MemberExpression>>. Making progress.
But that won't compile, and rightly so. We're returning a string, but we're using a filtering method, which requires a bool.
So let's now write EndsWith:
public static Expression<Func<T, bool>> EndsWith<T, TReturnType>(this Expression<Func<T, TReturnType>> expr, string str)
{
var endsWithMethod = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
var newBody = Expression.Call(expr.Body, endsWithMethod, Expression.Constant(str));
var result = Expression.Lambda<Func<T, bool>>(newBody, expr.Parameters);
return result;
}
And using it:
var classes = objects.Where(
ExpressionExtensions.Field<Test, Test>(q => q, "Matter")
.EndsWith("A")
);
Which is now compiling! And the expression tree looks like this:
UserQuery+Test[].Where(e => e.Matter.EndsWith("A"))
That's not too pretty, having Field take a redundant lambda, though. Let's add a helper method to make it look prettier:
public static Expression<Func<TElementType, TElementType>> Query<TElementType>(this Expression<Func<TElementType, TElementType>> expr)
{
return expr;
}
Putting it all together:
void Main()
{
var objects = new[] { new Test { Matter = "A" } }.AsQueryable();
var classes = objects.Where(
ExpressionExtensions.Query<Test>(q => q)
.Field("Matter")
.EndsWith("A")
);
classes.Expression.Dump();
}
public class Test
{
public string Matter { get; set;}
}
public static class ExpressionExtensions
{
public static Expression<Func<TElementType, TElementType>> Query<TElementType>(this Expression<Func<TElementType, TElementType>> expr)
{
return expr;
}
public static Expression<Func<TElementType, object>> Field<TElementType, TReturnType>(this Expression<Func<TElementType, TReturnType>> expr, string field)
{
Type entityType = expr.Body.Type;
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null)
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
ParameterExpression parameterExpression = Expression.Parameter(entityType, "e");
return Expression.Lambda<Func<TElementType, object>>(
Expression.Property(parameterExpression, propertyInfo),
parameterExpression
);
}
public static Expression<Func<T, bool>> EndsWith<T, TReturnType>(this Expression<Func<T, TReturnType>> expr, string str)
{
var endsWithMethod = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
var newBody = Expression.Call(expr.Body, endsWithMethod, Expression.Constant(str));
var result = Expression.Lambda<Func<T, bool>>(newBody, expr.Parameters);
return result;
}
}
I don't know if you mess the code to show a piece of code or if it was intended, but you have a generic extension that wants A T but you don't use it
Anyway if what you want is a method that returns you the value of a property, why don't you do a static exception that return T ?
public static class EntityExtension {
public static T Field<T>(this object entity, string field) {
Type entityType = entity.GetType();
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null) {
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
}
return (T)propertyInfo.GetValue(entity);
}
}
this is a fiddle i've done to show you the usage, pretty simple
https://dotnetfiddle.net/PoSfli
posting the code too in case fiddle get lost:
using System;
using System.Reflection;
using System.Linq.Expressions;
public class Program
{
public static void Main()
{
YourClass c = new YourClass() {
PropA = 1,
PropB = 2,
PropC = "ciao"
};
var propBValue = c.Field<int>("PropB");
Console.WriteLine("PropB value: {0}", propBValue);
var propCValue = c.Field<string>("PropC");
Console.WriteLine("PropC value: {0}", propCValue);
}
}
public static class EntityExtension {
public static T Field<T>(this object entity, string field) {
Type entityType = entity.GetType();
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null) {
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
}
return (T)propertyInfo.GetValue(entity);
}
}
public class YourClass {
public int PropA { get; set; }
public int PropB { get; set; }
public string PropC { get; set; }
}
nota that you can improve a lot, using a typed extension and a property expression as argument instead of a string
You can also do something really simple like if you want to use the property name:
IEnumerable<C> classes = this.backend.cs.Where(
c => c.Field<C>("Matter").ToString().EndsWith(string.Empty)
Or if your are filtering by property type:
IEnumerable<C> classes = this.backend.cs.Where(
c => c.Field<C>("Matter").Type.ToString().EndsWith(string.Empty)
How about something like this? Actually, your generic approach is not of use right now.
public static bool Evaluate<TField>(this object entity, string fieldName, Predicate<TField> condition)
{
Type entityType = entity.GetType();
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null)
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
var value = (TField)propertyInfo.GetValue(entity); //read the value and cast it to designated type, will raise invalid cast exception, if wrong
return condition.Invoke(value); //invoke the predicate to check the condition
}
Usage would be then.
.Where(item => item.Evaluate<string>("Matter", prop => prop.EndsWith(string.Empty))
You can add a new extension method which returns your desired type.
public static T Compile<T>(this Expression expression)
{
return Expression.Lambda<Func<T>>(expression).Compile()();
}
In your statement you just have to add .Compile<type>()
IEnumerable<C> classes = this.backend.cs.Where(
c => c.Field<C>("Matter").Compile<string>().EndsWith(string.Empty));
If I do nameof(instance.SomeProperty), it evaluates to "SomeProperty".
Is there any way I can get the entire method chain "instance.SomeProperty"?
I know I could do nameof(instance) + "." + nameof(instance.SomeProperty), but is there a better way that's more maintainable?
Is there any way I can get the entire method chain "instance.SomeProperty"?
Nope. You can, however, do something similar to your other solution:
$"{nameof(instance)}.{nameof(instance.SomeProperty)}"
You can try it here.
My 5 cents:
using System;
using System.Linq.Expressions;
public static class Program {
public static void Main() {
Console.WriteLine(Name.Of<A>(x => x.B.Hehe)); // outputs "B.Hehe"
var a = new A();
Console.WriteLine(Name.Of(() => a.B.Hehe)); // outputs "B.Hehe"
}
public class A {
public B B { get; } // property
}
public class B {
public int Hehe; // or field, does not matter
}
}
public static class Name
{
public static string Of(this Expression<Func<object>> expression) => Of(expression.Body);
public static string Of<T>(this Expression<Func<T, object>> expression) => Of(expression.Body);
public static string Of(this Expression expression)
{
switch (expression)
{
case MemberExpression m:
var prefix = Of(m.Expression);
return (prefix == "" ? "" : prefix + ".") + m.Member.Name;
case UnaryExpression u when u.Operand is MemberExpression m:
return Of(m);
default:
return "";
}
}
}
No, there is not. The nameof operator just yields the property (or class, field, etc) at the end of the expression, so nameof(Program.Main) will yield Main, and so does nameof(ConsoleAppliation1.Program.Main).
The nameof operator wasn't meant to do what you ask. It is just prevent passing names around for event handlers, dependency properties, etc. that depend on the sole name of the property / class name. All other fancy stuff you want to do is on your own.
Like M.kazem Akhgary commented, you can do this yourself by constructing the expression yourself:
$"{nameof(instance)}.{nameof(instance.SomeProperty)}"
I have a data access class that acts as an intermediary between logic classes and the underlying datasource, which is interchangeable. This class allows you to query the datasource using lambdas, LINQ-style. A source-agnostic class provides high-level functionality powered by a few basic operations (Add, GetAll, Update, Delete, Commit) that are implemented by small adapter classes, one for each source type (SQL, SQlite, XML serialiser, WCF client, REST client, whatever).
My problem is that some relational data sources (particularly SQLite) aren't smart enough to load relationship properties when I need them; I have to explicitly ask for them to be included. This is fine for my Get methods; I can pass a params array of expressions to load anything I need. With .Any(), however, this feels a bit odd - if I'm asking if there are any Customer records whose Purchases list contains a certain item, I shouldn't then have to tell it to load the Purchases list; that seems like the sort of thing it should be able to figure out.
So my Any() method takes Expression<Func<T, bool>> where T is obviously going to be the type I'm operating on. In the above example, it'd be used something like this:
using (var db = _dataAccessProvider.NewTransaction())
{
return db.Any<Customer>(c => c.Purchases.Contains(someProduct));
}
Is it possible to take the Expression<Func<Customer, bool>> that represents the operation c => c.Purchases.Contains(someProduct)) and work out that the property it's referring to is c => c.Purchases? How would I go about doing that? What about a lambda that touches multiple properties?
Use ExpressionVisitor to find all MemberExpression expressions which reference required object properties.
Quick example:
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
class Program
{
sealed class ReferencedPropertyFinder : ExpressionVisitor
{
private readonly Type _ownerType;
private readonly List<PropertyInfo> _properties = new List<PropertyInfo>();
public ReferencedPropertyFinder(Type ownerType)
{
_ownerType = ownerType;
}
public IReadOnlyList<PropertyInfo> Properties
{
get { return _properties; }
}
protected override Expression VisitMember(MemberExpression node)
{
var propertyInfo = node.Member as PropertyInfo;
if(propertyInfo != null && _ownerType.IsAssignableFrom(propertyInfo.DeclaringType))
{
// probably more filtering required
_properties.Add(propertyInfo);
}
return base.VisitMember(node);
}
}
private static IReadOnlyList<PropertyInfo> GetReferencedProperties<T, U>(Expression<Func<T, U>> expression)
{
var v = new ReferencedPropertyFinder(typeof(T));
v.Visit(expression);
return v.Properties;
}
sealed class TestEntity
{
public int PropertyA { get; set; }
public int PropertyB { get; set; }
public int PropertyC { get; set; }
}
static void Main(string[] args)
{
Expression<Func<TestEntity, int>> expression =
e => e.PropertyA + e.PropertyB;
foreach(var property in GetReferencedProperties(expression))
{
Console.WriteLine(property.Name);
}
}
}
Is it possible to do something like this:
class Foo
{
public delegate void Setter(float value);
public float Mass { get; set; }
public Foo()
{
Setter massSetter = new Setter(Mass.set);
// massSetter would be stored somewhere and used later
}
}
Can it be done while still using auto-implemented getters and setters?
Edit: I am trying to build a game using an "Entity Component" system, each "thing" in the game is an entity, and each entity is made up of components. Components are things like drawing to the screen, processing input, physics stuff, etc. One key in my plan for building these components is that all configuration of them will be done using their properties. Then I want to be able to save, load, duplicate, and edit on the fly these Components in a generic way. If the meat of the components is in their properties, then to save we just save the value of each property, same for loading and duplication. And when I add a new component, getting it to have all of these desired behaviors will be as simple as adding all the getters, setters, and property names, to a list. This will also allow me to build a generic editor which will allow me to edit any property on the fly. (Yes I realize I will have to handle all string properties differently from all int properties but I am only planning on using a handful of types.) I hope that made sense and I am open to suggestions on alternate designs.
As some comments hint, this will do the job:
class Foo
{
public delegate void Setter(float value);
public float Mass { get; set; }
public Foo()
{
Setter massSetter = x => Mass = x;
// massSetter would be stored somewhere and used later
}
}
IE, use an action as a delegate.
In this case though, it seems a bit superfluous, why do you need this functionality?
var f = new Foo();
f.Mass = 1.0d; // What you do with the property
theSetter(1.0d); // What you can do now
Isn't the property clearer in it's purpose?
Some time ago I made this objectification of a property which allows passing it around, getting and setting the value.
using System;
using System.Linq.Expressions;
using System.Reflection;
public class PropHandle<TOwner, TProperty>
{
private TOwner owner;
private PropertyInfo propertyInfo;
public PropHandle(TOwner owner, Expression<Func<TOwner, TProperty>> propertyExpression)
{
this.owner = owner;
var memberExpression = (MemberExpression)propertyExpression.Body;
this.propertyInfo = (PropertyInfo)memberExpression.Member;
}
public TProperty Get()
{
return (TProperty)this.propertyInfo.GetValue(this.owner, null);
}
public void Set(TProperty value)
{
this.propertyInfo.SetValue(this.owner, value, null);
}
}
Together with a helpful extension method
public static PropHandle<TOwner, TProperty> PropHandle<TOwner, TProperty>(this TOwner owner, Expression<Func<TOwner, TProperty>> propertyExpression)
{
return new PropHandle<TOwner, TProperty>(owner, propertyExpression);
}
Created and used like this
var myProp = this.PropHandle(x => x.Mass);
myProp.Set(12);
And used in a method
public void MyMethod<TOwner>(PropHandle<TOwner, float> massProperty)
{
massProperty.Set(123);
}
EDIT:
To get the name and path (one level) of the property you can add this method to the PropHandle-class
public string GetSimplePath()
{
return this.owner.GetType().Name + this.propertyInfo.Name;
}
flindeberg's answer is the simplest and the clearest. But, if you're dead-set (no pun intended) on using the setter method without wrapping it in a lambda, this can be done. It's just not the easiest thing in the whole world.
using System;
using System.Linq.Expressions;
using System.Reflection;
public Action<TProperty> GetSetterAction(TSource target,
Expression<Func<TSource, TProperty>> propertyExpression)
{
var memberExpression = propertyExpression.Body as MemberExpression;
if (memberExpression == null)
{
throw new ArgumentException(#"propertyExpression must express a single
member of a type.", "propertyExpression");
}
var propertyInfo = memberExpression.Member as PropertyExpression;
if (propertyInfo == null)
{
throw new ArgumentException(#"propertyExpression must express a single
property of a type.", "propertyExpression");
}
var setMethodInfo = propertyInfo.GetSetMethod();
if (setMethodInfo == null)
{
throw new ArgumentException(#"propertyExpression must express a single
writeable property of a type.", "propertyExpression");
}
var setMethodDelegate = setMethodInfo.CreateDelegate(
typeof(Action<TProperty>), target);
return (Action<TProperty>)(object)setMethodDelegate;
}