Using Enum as/with expressions? - c#

Is it possible to use an enum with expressions to reflect on the enum values? Consider this hypothetical routine:
public enum Fruit
{
Apple,
Pear
}
public void Foo(Fruit fruit)
{
Foo<Fruit>(() => fruit);
}
public void Foo<T>(Expression<Func<T>> expression)
{
//... example: work with Fruit.Pear and reflect on it
}
Bar() will give me information about the enum, but I want to work with the actual value.
Background: I've been adding some helper methods to return CustomAttribute information for types and wondered if a similar routine could be used for enums.
I'm fully aware you can work with the enum type to get the CustomAttributes that way.
Update:
I use a similar concept in MVC with helper extensions:
public class HtmlHelper<TModel> : System.Web.Mvc.HtmlHelper<TModel>
{
public void BeginLabelFor<TProperty>(Expression<Func<TModel, TProperty>> expression)
{
string name = ExpressionHelper.GetExpressionText(expression);
}
}
In this example name would be the member name of the model. I want to do a similar thing with enums, so name would be the enum 'member'. Is this even possible?
Updated example:
public enum Fruit
{
[Description("I am a pear")]
Pear
}
public void ARoutine(Fruit fruit)
{
GetEnumDescription(() => fruit); // returns "I am a pear"
}
public string GetEnumDescription<T>(/* what would this be in a form of expression? Expression<T>? */)
{
MemberInfo memberInfo;
// a routine to get the MemberInfo(?) 'Pear' from Fruit - is this even possible?
if (memberInfo != null)
{
return memberInfo.GetCustomAttribute<DescriptionAttribute>().Description;
}
return null; // not found or no description
}

You don't need Expressions for this. All you need to know is that enums have a field for each of their values. This means you can do something like:
public static string GetEnumDescription<T>(T enumValue) where T : struct, Enum
{
FieldInfo field = typeof(T).GetField(enumValue.ToString());
if (field != null)
{
var attribute = field.GetCustomAttribute<DescriptionAttribute>();
if (attribute != null)
return attribute.Description;
}
return null; // not found or no description
}

Related

Type.GetMethod() for polymorphic method (both generic and non-generic)

I am currently creating a custom way of deep-copying my objects. I use a static class for this functionality.
public static class CopyServer
{
public static int CopyDeep(int original)
{
return original;
}
//not shown: same for all other value types I use (long, float,...)
public static T CopyDeep<T>(T original) where T: ICopyAble
{
if (original == null)
return default;
if (original is ICopyAutofields)
return CopyAutofields(original);
return (T)original.CopyDeep();
}
private static T CopyAutofields<T>(T original)
{
Delegate del;
if (!_copyFunctions.TryGetValue(typeof(T), out del))
{
//not shown: Building expression for parameter etc.
foreach (var fieldInfo in typeof(T).GetFields())
{
//not shown: checking options set by custom attributes
MethodInfo methodInfo = typeof(CopyServer).GetMethod("CopyDeep", new[] { fieldInfo.FieldType });
//I can't remove the second param without getting an AmbiguousMatchException
if (methodInfo == null)
{
throw new Exception($"CopyDeep not defined for type {fieldInfo.FieldType}");
}
if (methodInfo.IsGenericMethod)
methodInfo = methodInfo.MakeGenericMethod(fieldInfo.FieldType);
Expression call = Expression.Call(methodInfo, readValue);
//not shown: Assign Expression
}
//not shown: return Expression and compiling
}
return ((Func<T, T>)del)(original);
}
}
I use T CopyAutofields<T> to build functions (by building and compiling Expression Trees) so I don't have to create copy-functions for each and every class I want to copy by hand. I control the copy-behaviour with Custom Attributes (I left this part in the code above since it is not relevant for my problem).
The code works fine as long as long as only fields with types for which a non-generic function exists are used. But it is not able to retrieve my generic function T CopyDeep<T>.
Example:
//This works:
public class Manager : ICopyAble,ICopyAutofields
{
public string FirstName;
public string LastName;
}
//This doesn't
//Meaning: typeof(CopyServer).GetMethod("copyDeep", new[] { fieldInfo.FieldType });
//in T copyAutofields<T> returns null for the Manager-field and my exception gets thrown
public class Employee : ICopyAble,ICopyAutofields
{
public string FirstName;
public string LastName;
public Manager Manager;
}
//This is what I was using before I started using the ICopyAutofields.
//This approach works, but its' too much too write since my classes usually
//have way more than three fields and I occasionally forget to update
//copyDeep()-function if I add new ones.
public class Employee : ICopyAble,ICopyAutofields
{
public string FirstName;
public string LastName;
public Manager Manager;
public IModable CopyDeep()
{
var result = new Employee();
result.FirstName = CopyServer.copyDeep(FirstName);
result.LastName= CopyServer.copyDeep(LastName);
result.Manager= CopyServer.copyDeep(Manager);
return result;
}
}
Long story short: I need a way of getting a matching function for a type T if both generic and non-generic functions with the right name exist.
In .NET 4.7.1 you need to use method GetMethods and filter the results:
class MyClass
{
public T M<T>(T t) { return default(T); }
public int M(int t) { return 0; }
}
var m = typeof(MyClass).GetMethod("M", new[] { typeof(string) }); // null
var m1 = typeof(MyClass).GetMethods()
.Where(mi => mi.Name == "M" && mi.GetGenericArguments().Any())
.First(); // returns generic method
In .NET Standard 2.1 (and .NET Core since 2.1) there is another way to resolve generic type arguments - Type.MakeGenericMethodParameter, like you can see it in this answer.
Also as workaround you can move your copyAutofields<T> method to generic class like CopyAutoFieldServer<T>:
public static class CopyAutoFieldServer<T>
{
public static T copyAutofields(T original) { ... }
}

Generic constraints with C# Expression<TDelegate> - Cannot implicitly convert Type

I've started using C# Expression constructs, and I've got a question about how generics are applied in the following situation:
Consider I have a type MyObject which is a base class for many different types. Inside this class I have the following code:
// This is a String Indexer Expression, used to define the string indexer when the object is in a collection of MyObjects
public Expression<Func<MyObject, string, bool>> StringIndexExpression { get; private set;}
// I use this method in Set StringIndexExpression and T is a subtype of MyObject
protected void DefineStringIndexer<T>(Expression<T, string, bool>> expresson) where T : MyObject
{
StringIndexExpression = expression;
}
This is how I use DefineStringIndexer:
public class MyBusinessObject : MyObject
{
public string Name { get; set; }
public MyBusinessObject()
{
Name = "Test";
DefineStringIndexer<MyBusinessObject>((item, value) => item.Name == value);
}
}
However in the assignment inside DefineStringIndexer I get the compile error:
Cannot implicitly convert type
System.Linq.Expression.Expression< MyObject, string, bool > to
System.Linq.Expression.Expression < MyBusinessObject, string, bool >>
Can I use Generics with C# Expressions in this situation? I want to use T in DefineStringIndexer so I can avoid casting MyObject inside the lambda.
The assignment will not work, because the Func<MyBusinessObject,string,bool> type is not assignment-compatible with Func<MyObject,string,bool>. However, the parameters of the two functors are compatible, so you can add a wrapper to make it work:
protected void DefineStringIndexer<T>(Func<T,string,bool> expresson) where T : MyObject {
StringIndexExpression = (t,s) => expression(t, s);
}
Would this work better for you?
Edit: Added <T> to constraint - think you will need that :)
class MyObject<T>
{
// This is a String Indexer Expression, used to define the string indexer when the object is in a collection of MyObjects
public Expression<Func<T, string, bool>> StringIndexExpression { get; private set;}
// I use this method in Set StringIndexExpression and T is a subtype of MyObject
protected void DefineStringIndexer<T>(Expression<T, string, bool>> expresson)
where T : MyObject<T> // Think you need this constraint to also have the generic param
{
StringIndexExpression = expression;
}
}
then:
public class MyBusinessObject : MyObject<MyBusinessObject>
{
public string Name { get; set; }
public MyBusinessObject()
{
Name = "Test";
DefineStringIndexer<MyBusinessObject>((item, value) => item.Name == value);
}
}

Attribute Constructor With Lambda

It is possible to do this:
public static void SomeMethod<TFunc>(Expression<TFunc> expr)
{
//LambdaExpression happily excepts any Expession<TFunc>
LambdaExpression lamb = expr;
}
and call it elsewhere passing a lambda for the parameter:
SomeMethod<Func<IQueryable<Person>,Person>>( p=>p.FirstOrDefault());
I would instead like to pass an expression as a parameter to an attribute constructor.
Is it possible to do the below?
class ExpandableQueryAttribute: Attribute {
private LambdaExpression someLambda;
//ctor
public ExpandableQueryMethodAttribute(LambdaExpression expression)
{
someLambda = expression
}
}
//usage:
static LambdaExpression exp =
(Expression<Func<IQueryable<Person>, Person>>)
(p => p.FirstOrDefault());
[ExpandableQueryAttribute(exp)] //error here
// "An attribute argument must be a constant expression, typeof expression
// or array creation expression of an attribute parameter type"
My goal is to specify a method or lambda in the constructor of the attribute(even if I have to declare a full named method and pass the name of the method somehow, that'd be fine to).
Parameter types can change, but it is important that the attribute constructor can take that parameter and in some way be able to assign it to a field of type LambdaExpression
I want the declaration of the lambda/method to be just above the call to the attribute constructor, or inline, so that you don't have to go far to see what is being passed.
So these alternatives would be fine, but no luck getting them to work:
public static ... FuncName(...){...}
[ExpandableQueryAttribute(FuncName)]
// ...
or
//lambdas aren't allowed inline for an attribute, as far as I know
[ExpandableQueryAttribute(q => q.FirstOrDefault())]
// ...
The existing work around is to pass a number ID to the constructor(satisfying the "argument must be a constant" requirement), which is used by the constructor to do a lookup in a dictionary where expressions have been added previously. Was hoping to improve/simplify this, but I have a feeling it doesn't get any better due to limitations on attribute constructors.
how about this:
class ExpandableQueryAttribute : Attribute
{
private LambdaExpression someLambda;
//ctor
public ExpandableQueryAttribute(Type hostingType, string filterMethod)
{
someLambda = (LambdaExpression)hostingType.GetField(filterMethod).GetValue(null);
// could also use a static method
}
}
this should let you assign your lambda to a field and then suck it in at runtime, although in general I would prefer to use something like PostSharp to do this at compile time.
simple usage example
public class LambdaExpressionAttribute : Attribute
{
public LambdaExpression MyLambda { get; private set; }
//ctor
public LambdaExpressionAttribute(Type hostingType, string filterMethod)
{
MyLambda = (LambdaExpression)hostingType.GetField(filterMethod).GetValue(null);
}
}
public class User
{
public bool IsAdministrator { get; set; }
}
public static class securityExpresions
{
public static readonly LambdaExpression IsAdministrator = (Expression<Predicate<User>>)(x => x.IsAdministrator);
public static readonly LambdaExpression IsValid = (Expression<Predicate<User>>)(x => x != null);
public static void CheckAccess(User user)
{
// only for this POC... never do this in shipping code
System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace();
var method = stackTrace.GetFrame(1).GetMethod();
var filters = method.GetCustomAttributes(typeof(LambdaExpressionAttribute), true).OfType<LambdaExpressionAttribute>();
foreach (var filter in filters)
{
if ((bool)filter.MyLambda.Compile().DynamicInvoke(user) == false)
{
throw new UnauthorizedAccessException("user does not have access to: " + method.Name);
}
}
}
}
public static class TheClass
{
[LambdaExpression(typeof(securityExpresions), "IsValid")]
public static void ReadSomething(User user, object theThing)
{
securityExpresions.CheckAccess(user);
Console.WriteLine("read something");
}
[LambdaExpression(typeof(securityExpresions), "IsAdministrator")]
public static void WriteSomething(User user, object theThing)
{
securityExpresions.CheckAccess(user);
Console.WriteLine("wrote something");
}
}
static void Main(string[] args)
{
User u = new User();
try
{
TheClass.ReadSomething(u, new object());
TheClass.WriteSomething(u, new object());
}
catch(Exception e)
{
Console.WriteLine(e);
}
}
This is not possible because what you can pass into an attribute needs to fit into the CLR's binary DLL format and there is no way to encode arbitrary object initialization. For the same you you cannot pass a nullable value for example. The restrictions are very strict.
Although you cannot have complex constructor for attributes, in some situation a work-aound is to have a public property for that attribute and update it at run-time.
self point to an object of the class that contains some attributes on its properties. LocalDisplayNameAttribute is a custom attribute.
The following code will set ResourceKey property of my custom attribute class at run-time. Then at that point you can override DisplayName to outpiut whatever text you want.
static public void UpdateAttributes(object self)
{
foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(self))
{
LocalDisplayNameAttribute attr =
prop.Attributes[typeof(LocalDisplayNameAttribute)]
as LocalDisplayNameAttribute;
if (attr == null)
{
continue;
}
attr.ResourceKey = prop.Name;
}
}
Use dymanic linq: http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
You can make the attribute's constructor take a string which evaluates to an expression.

Can an extension method be added to a class property to get the value of an attribute associated with the property?

I have several classes with attributes assigned to them. The one I'm mostly interested in is the FieldLength.MaxLength value.
/// <summary>
/// Users
/// </summary>
[Table(Schema = "dbo", Name = "users"), Serializable]
public partial class Users
{
/// <summary>
/// Last name
/// </summary>
[Column(Name = "last_name", SqlDbType = SqlDbType.VarChar)]
private string _LastName;
[FieldLength(MaxLength=25), FieldNullable(IsNullable=false)]
public string LastName
{
set { _LastName = value; }
get { return _LastName; }
}
}
I need to know if it's possible to write some kind of extension method for the properties in my class to return the MaxLength value of the FieldLength attribute?
For instance. I'd like to be able to write something like the following…
Users user = new Users();
int lastNameMaxLength = user.LastName.MaxLength();
No, this is not possible. You could add an extension method on Users though:
public static int LastNameMaxLength(this Users user) {
// get by reflection, return
}
To save typing, you could further refine Jason's Extension to something like this.
public static void MaxLength<T>(this T obj, Expression<Func<T, object>> property)
This way it will appear on all object (unless you specify a where T restriction) and you have a compile-time safe implementation of the Property Accessor as you would use the code as:
user.MaxLength(u => u.LastName);
No. Because the syntax you propose returns the value of the LastName property and not the property itself.
In order to retrieve and make use of the attributes, you'll need to use reflection which means you need to know the property itself.
As an idea, you could achieve this neatly by using LINQ's Expression library though to resolve the property for the object.
Example syntax you might look for:
var lastNameMaxLength = AttributeResolver.MaxLength<Users>(u => u.LastName);
Where:
public class AttributeResolver
{
public int MaxLength<T>(Expression<Func<T, object>> propertyExpression)
{
// Do the good stuff to get the PropertyInfo from the Expression...
// Then get the attribute from the PropertyInfo
// Then read the value from the attribute
}
}
I've found this class helpful in resolving properties from Expressions:
public class TypeHelper
{
private static PropertyInfo GetPropertyInternal(LambdaExpression p)
{
MemberExpression memberExpression;
if (p.Body is UnaryExpression)
{
UnaryExpression ue = (UnaryExpression)p.Body;
memberExpression = (MemberExpression)ue.Operand;
}
else
{
memberExpression = (MemberExpression)p.Body;
}
return (PropertyInfo)(memberExpression).Member;
}
public static PropertyInfo GetProperty<TObject>(Expression<Func<TObject, object>> p)
{
return GetPropertyInternal(p);
}
}
That's not possible in that form. The best you can manage is a method that takes a lambda expression, gets the property associated with it and then use reflection to obtain the attribute.
int GetMaxLength<T>(Expression<Func<T,string>> property);
And call it like:
GetMaxLength<Users>((u)=>LastName)
You could write an extension method, but it would have to accept a first parameter of PropertyInfo rather than string (since the string itself has no attributes.) It would look something like this:
public static int GetMaxLength(this PropertyInfo prop)
{
// TODO: null check on prop
var attributes = prop.GetCustomeAttributes(typeof(FieldLengthAttribute), false);
if (attributes != null && attributes.Length > 0)
{
MaxLengthAttribute mla = (MaxLengthAttribute)attributes[0];
return mla.MaxLength;
}
// Either throw or return an indicator that something is wrong
}
You then get the property via reflection:
int maxLength = typeof(Users).GetProperty("LastName").GetMaxLength();

Is there a way of making C# binding work statically?

This probably applies to other places, but in WinForms, when I use binding I find many methods want to take the name of the property to bind to. Something like:
class Person
{
public String Name { get { ... } set { ... } }
public int Age { get { ... } set { ... } }
}
class PersonView
{
void Bind(Person p)
{
nameControl.Bind(p,"Name");
ageControl.Bind(p,"Age");
}
}
The big problem I keep having with this is that "Name" and "Age" are specified as strings. This means the compiler is no help if someone renames one of Person's properties. The code will compile fine, but the bindings will be broken.
Is there a standard way of solving this that I've missed? It feels like I need some keyword, maybe called stringof to match the existing typeof. You could use it something like:
ageControl.Bind(p,stringof(p.Age).Name);
stringof could return some class that has properties for getting the full path, part of the path, or the string so you can parse it up yourself.
Is something like this already do-able?
Have a look at this code snippet I've posted in another question, it can help you! (But only, if you are using .NET 3.5)
Best Regards
Oliver Hanappi
You can do that with expression trees, as explained in this question
protected static string GetPropertyName<TSource, TResult>(Expression<Func<TSource, TResult>> expression)
{
if (expression.NodeType == ExpressionType.Lambda && expression.Body.NodeType == ExpressionType.MemberAccess)
{
PropertyInfo prop = (expression.Body as MemberExpression).Member as PropertyInfo;
if (prop != null)
{
return prop.Name;
}
}
throw new ArgumentException("expression", "Not a property expression");
}
...
ageControl.Bind(p, GetPropertyName((Person p) => p.Age));
You can use Expressions to get compiler-checked bindings.
For example, in one of current projects we set up bindings like this:
DataBinder
.BindToObject(this)
.ObjectProperty(c => c.IsReadOnly)
.Control(nameTextBox, n => n.ReadOnly)
.Control(addressControl, n => n.ReadOnly)
Code supporting this style is separated into several classes:
public static class DataBinder
{
public static DataBinderBindingSourceContext<TDataSource> BindToObject<TDataSource>(TDataSource dataSource)
{
return new DataBinderBindingSourceContext<TDataSource>(dataSource);
}
}
public class DataBinderBindingSourceContext<TDataSource>
{
public readonly object DataSource;
public DataBinderBindingSourceContext(object dataSource)
{
DataSource = dataSource;
}
public DataBinderControlContext<TDataSource, TProperty> ObjectProperty<TProperty>(Expression<Func<TDataSource, TProperty>> property)
{
return new DataBinderControlContext<TDataSource, TProperty>(this, property);
}
}
public class DataBinderControlContext<TDataSource, TProperty>
{
readonly DataBinderBindingSourceContext<TDataSource> BindingSourceContext;
readonly string ObjectProperty;
public DataBinderControlContext
(
DataBinderBindingSourceContext<TDataSource> bindingSourceContext,
Expression<Func<TDataSource, TProperty>> objectProperty
)
{
BindingSourceContext = RequireArg.NotNull(bindingSourceContext);
ObjectProperty = ExpressionHelper.GetPropertyName(objectProperty);
}
public DataBinderControlContext<TDataSource, TProperty> Control<TControl>(TControl control, Expression<Func<TControl, TProperty>> property)
where TControl : Control
{
var controlPropertyName = ExpressionHelper.GetPropertyName(property);
control.DataBindings.Add(controlPropertyName, BindingSourceContext.DataSource, ObjectProperty, true);
return this;
}
}
public static class ExpressionHelper
{
public static string GetPropertyName<TResult>(Expression<Func<TResult>> property)
{
return GetMemberNames(((LambdaExpression)property).Body).Skip(1).Join(".");
}
public static string GetPropertyName<T, TResult>(Expression<Func<T, TResult>> property)
{
return GetMemberNames(((LambdaExpression)property).Body).Join(".");
}
static IEnumerable<string> GetMemberNames(Expression expression)
{
if (expression is ConstantExpression || expression is ParameterExpression)
yield break;
var memberExpression = (MemberExpression)expression;
foreach (var memberName in GetMemberNames(memberExpression.Expression))
yield return memberName;
yield return memberExpression.Member.Name;
}
}
public static class StringExtentions
{
public static string Join(this IEnumerable<string> values, string separator)
{
if (values == null)
return null;
return string.Join(separator, values.ToArray());
}
}
You could use reflection to find the name ;-)
This of course would be a circular reference, you'd use the name that you think it is to find the same name (or to not find anything, meaning the property was renamed... But there's an idea (or rather, a trick) : by making a do-nothing reference to the property you wish to use, you'd get compile time confirmation that it is still there. Only problem is if someone merely swap various property names around; in that case, the names still exist (no compile-time error), but have different application-level semantics (possible surprises in the application's output)

Categories