Given this class:
public class MyClass
{
public int MyProperty {get; set;}
}
How will I be able to extract the name of MyProperty in code?
For example, I am able to get the name of the class like this
typeof(MyClass).Name
How can I do something similar for the property?
The reason for the question is that I want this particular code to be resistant against refactorizations of the names.
EDIT: With resistant I mean that I want the code at the call site to be robust in the face of changes of the propertyname. I have some stuff that is using a string representation of the property name. Sorry for the poor phrasing.
I did not include call site code in order to keep the problem clean and not wander off into other discussions on the nature of the call site code.
You do it like this, using compiler generated expression trees:
public static string GetMemberName<T, TValue>(Expression<Func<T, TValue>> memberAccess)
{
return ((MemberExpression)memberAccess.Body).Member.Name;
}
Now call the static method from code:
class MyClass
{
public int Field;
public string Property { get; set; }
}
var fieldName = GetMemberName((MyClass c) => c.Field);
var propertyName = GetMemberName((MyClass c) => c.Property);
// fieldName has string value of `Field`
// propertyName has string value of `Property`
You can now also use refactoring to rename that field without breaking this code
In C# 6 we can do it very simply
nameof(MyField);
you can get method\type\propery\field\class\namespace names in the same way
ex
nameof(MyClass);
nameof(namespacename1) // returns "namespacename1"
nameof(TestEnum.FirstValue) // returns enum's first value
MSDN Reference
Look at this post
With C# 6.0, you can use the new nameof operator.
nameof(MyClass.MyField) // returns "MyField"
nameof(MyClass) //returns "MyClass"
See nameof (C# and Visual Basic Reference) for more examples.
Using Reflection you can find all Members from MyClass with this.
MemberInfo[] members = typeof(MyClass).GetMembers();
Now you can find your desired property for each Member.
foreach ( MemberInfo memberInfo in members)
{
Console.WriteLine("Name: {0}", memberInfo.Name); // Name: MyField
Console.WriteLine("Member Type: {0}", memberInfo.MemberType); // Member Type: Property
}
If you want to find only Properties use PropertyInfo instead of MemberInfo.
Or write this
foreach ( MemberInfo memberInfo in members.Where(p => p.MemberType == MemberTypes.Property))
{
Console.WriteLine("Name: {0}", memberInfo.Name); // Name: MyField
Console.WriteLine("Member Type: {0}", memberInfo.MemberType); // Member Type: Property
}
You could use the following class which contains a method using an expression tree as an argument to determine a member name based on a lambda expression:
public class MemberHelper<T>
{
public string GetName<U>(Expression<Func<T, U>> expression)
{
MemberExpression memberExpression = expression.Body as MemberExpression;
if (memberExpression != null)
return memberExpression.Member.Name;
throw new InvalidOperationException("Member expression expected");
}
}
Then use it like so:
MemberHelper<MyClass> memberHelper = new MemberHelper<MyClass>();
string name = memberHelper.GetName(x => x.MyField);
If you only want to get name of an instance member, you can use shorter code:
public static string GetMemberName<TValue>(Expression<Func<TValue>> memberAccess)
{
return ((MemberExpression)memberAccess.Body).Member.Name;
}
And use it like the following inside the class:
ReflectionTools.GetMemberName(() => _someInstanceVariableOrProperty)
If you don’t want to waste your time, try the following one. I know there will be people run into the same problem.
// Soner - tested!
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(item))
{
string name = descriptor.Name; // Name
object value = descriptor.GetValue(item); // Value
var type = descriptor.PropertyType; // Type
Console.WriteLine($"{name}={value}={type}");
}
Don’t forget to add using System.ComponentModel;.
Related
I am new to C# and I am trying to create a method that accept generic parameter type like in Javascript.
Let says I have a List of User objects with name and id properties.
In Javascript , to search through the list using property value will be as simple as:
class User{
constructor(name, id){
this.name = name;
this.id = id;
}
}
let users = [
new User("John",1),
new User("Deer", 2),
new User("Bruce", 3),
]
function findArr(arr, propertyName, propertyValue){
for(let a of arr)
if(a[propertyName] === propertyValue)
return a;
return null;
}
console.log(findArr(users, "name", "Deer"))
console.log(findArr(users, "id", 3))
Then, I try to recreate this in C#:
using System;
using System.Collections.Generic;
using System.Linq;
public class User{
public string name {get; set;}
public int id {get; set;}
}
public class HelloWorld
{
public static void Main(string[] args)
{
List<User> Users = new List<User>{
new User{name="John", id=1},
new User{name="Deer", id=2},
new User{name="Bruce", id=3}
};
int propertyValue = 2;
var u = FindList<User, int>(Users, "id", ref propertyValue);
Console.WriteLine(u.name + " " + u.id);
}
public static T FindList<T, K>(List<T> obj, string propertyName, ref K propertyValue){
return (T)obj.FirstOrDefault(o=>
(K)o.GetType().GetProperty(propertyName).GetValue(o, null)
==
propertyValue
);
}
}
But this will throw exception: Cannot use ref, out, or in parameter 'propertyValue' inside an anonymous method, lambda expression, query expression, or local function.
Why is this exception thrown?
How do I make this work?
As the error message states, you cannot use the ref or out keywords in an anonymous function. This is by design, as detailed in this answer.
The good news is, you do not need to use ref at all. You can achieve what you want simply by passing the property value as a normal parameter, like so:
public static T FindList<T, K>(List<T> obj, string propertyName, K propertyValue){
return (T)obj.FirstOrDefault(o=>
EqualityComparer<K>.Default.Equals((K)o.GetType().GetProperty(propertyName).GetValue(o, null) , propertyValue));
}
Note that the equality check with == operator won't work for unconstrained generic type in general, so we use the EqualityComparer method instead.
Javascript is a weakly typed language, every object is essentially just a dictionary.
If you ever find yourself accessing properties by name in C#, you're probably doing something "wrong".
Rather than using reflection, you could pass in a function to access the property;
public static T FindList<T, V>(List<T> obj, Func<T,V> propertyAccess, V propertyValue){
return obj.FirstOrDefault(o => propertyAccess(o) == propertyValue);
}
var u = FindList<User, int>(Users, u => u.id, propertyValue);
However, I don't think this method really adds anything to your C# code. You'd be better off just writing the straight forward solution, without complicating anything with your own "helper" method;
var u = Users.FirstOrDefault(u => u.id == propertyValue);
It's not the same as the javascript code you are familiar with. But you're writing in C# now. If you want to work with other C# programmers, you will need to change the way you think about programming problems.
Had a hard time finding what I'm trying to do and this post was the closest I could find. This post won't work as I don't know the integer value of the enum, I only know its name. Given the following code:
public enum Foo
{
Row = 0,
Column = 20, // This is why the second post won't work, I only know the name "Column"
None = 30
}
public static class ExpressionGetter
{
public static Expression GetExpression(Type type, string name)
{
// Not sure what I should do here. I want an expression object for Foo.Row
}
}
void Main()
{
var expression = ExpressGetter.GetExpression(typeof(Foo), "Row");
}
Later in my application, I am building expression trees to generate LINQ queries and I know the type of the enum and name of the enum and now I want to create an Expression.Constant of it or if there's another way to do this, I'd like to know how.
I want at the end an expression that looks like this:
Foo.Row
I've tried:
Expression.Property(null, enumType, name)
But it does not work. Results in
ArgumentException: Property 'Row' is not defined for type 'Foo'
Parameter name: propertyName
which makes sense because it's a struct not an object.
So I'm not sure how to build the Expression Foo.Row given the enum type Foo and the name as a string.
An enum value is a static field of the enum type. If you only have the name of the enum value as a string, the second version is what you're looking for. But you could also do Enum.Parse() with the first version.
Expression.Constant(Foo.Row, typeof(Foo));
// Or any other string that's valid
var name = "Row";
MemberExpression.Field(null, typeof(Foo), name);
More or less like this:
public enum EnumerationTest
{
A, B, C
}
public class ClassTest
{
public EnumerationTest Test { get; set; }
}
public static Expression PropertyExpression()
{
// Think of this like a lambda (p) => p.Test == Enumeration.A
var parameter = Expression.Parameter(typeof(ClassTest), "p");
var property = Expression.PropertyOrField(parameter, "Test");
var value = (EnumerationTest)Enum.Parse(typeof(EnumerationTest), "A");
var constant = Expression.Constant(value, typeof(EnumerationTest));
return Expression.Equal(property, constant);
}
You generally are using a lot of reflection and string parsing when doing Expression trees. At least that's what I have found in my experience
For the sake of simplicity take following example, which is not valid in C#:
public void SomeGenericFunc<T>(T classObject, T.Property.Type classPorpertyType)
{
string propertyName = nameof(classPropertyType);
// ...
}
Sample simple class:
public class Car
{
public Car(){}
public int DoorsCount { get; set; }
public string Color { get; set; }
}
A function call would look something like this:
SomeGenericFunc<Car>(new Car(), Car.DoorsCount); //should be valid
SomeGenericFunc<Car>(new Car(), SomeType); //should be invalid at compile time: SomeType is not a property of Car
SomeGenericFunc<Car>(new Car(), Car.Color); //should be valid
Obviously there is nothing like T.Property.Type available in C#. In this example i don't want to pass nameof(Car.DoorsCount) everytime i call the function. I want the nameof() call to take place in SomeGenericFunc<T>(),so i can call SomeGenericFunc<T>() with any class and any of that class' property.
So is it possible to pass and expect a property type of a class in a generic function?
EDIT:
To make it clear: I DON'T want the value of the property Car.DoorsCount. I want the name of the property, without passing it directly as parameter. I want to resolve the name of the property in SomeGenericFunc<T>().
Try using expression trees:
static void GetPropertyName<T, TProperty>(Expression<Func<T, TProperty>> property) {
var propertyInfo = (property.Body as MemberExpression).Member;
Console.WriteLine(propertyInfo.Name);
}
You can call it like this:
GetPropertyName((Car c) => c.DoorsCount);
Obviously, this will throw an exception if you try to pass in stuff that isn't a member expression.
You can't use nameof here because nameof is a constant expression. The expression in the brackets is not evaluated. The whole nameof expression is evaluated at compile time. This means that trying to get the name of the property passed as a parameter will just get you the name of the parameter. You need something that works at runtime, like expression trees.
Also note that you don't need an instance of Car here. What the method needs is just an expression. No property's value is actually evaluated.
Your proposal has no solution after all: you don't want to pass the name of a given property using nameof and you want to give a property to later get the whole type to get property type.
If you're looking to both receive property value and get the its name later, you may use an expression tree:
public class A
{
public int Value { get; set; }
}
public static S DoStuff<T, S>(T some, Expression<Func<T, S>> propertySelector)
{
if(selector.Body is MemberExpression memberExpr)
{
var propertyType = memberExpr.Member;
var propertyName = propertyType.Name;
// Extra: as per your last edit, you don't want this, but
// I leave the code so you're aware that you can have both
// the property and its value
var somePropertyValue = propertySelector.Compile()(some);
return somePropertyValue;
}
else
throw new ArgumentException("propertySelector", "This isn't a member expression");
}
So, you would call that method as follows:
DoStuff(new A(), a => a.Value);
Trying to stay as close as possible to given code sample while providing minimal error handling:
public static void SomeGenericFunc<T, PT>(T classObject, Expression<Func<T, PT>> expression)
{
var targetType = typeof(T);
var property = expression.Body as MemberExpression;
var propertyInfo = property?.Member as PropertyInfo;
if (property == null || propertyInfo == null)
throw new ArgumentException("Expecting property!");
if (propertyInfo.ReflectedType != targetType)
throw new ArgumentException($"Property does not belong to type {targetType.Name}");
string propertyName = propertyInfo.Name;
}
You can call it with:
SomeGenericFunc(new Car(), car => car.DoorsCount);
I have a List that I am iterating through.
Inside the List<> are Argument classes which contain two properties 'PropertyName' and 'Value'
What I need to do is iterate through the collection of Arguments and assign the Value of that Argument to the Property (with the same name as current Argument) of a different class.
Example:
Argument:
PropertyName: ClientID
Value: 1234
Members Class:
ClientID = {Argument Value here}
I hope this makes sense. I have a way of doing it, hard coding the properties of my class and matching it up with the Argument list.
Something like:
foreach(var arg in List<Argument>)
{
Members.ClientID = arg.Find(x => compareName(x, "ClientID")).Value;
//where compareName just does a simple string.Compare
}
But what would the BEST way be for something like this?
EDIT: Sorry about this guys and thanks for the replies so far. Here is what I didn't mention and might make a difference.
Each argument is a different property for the same class. I am iterating through the List and each one in there will be for the same Members class I have to populate.
I wanted to mention this because im thinking in the foreach I might have to use a switch to determine what 'PropertyName' I have for that Argument. ClientID is one of them but I believe there are 14 total properties in the Members class that need populated from the Collection.
Does that change things?
Thanks again
public object this[string propertyName]
{
get
{
Type myType = typeof(UserConfiguration);
PropertyInfo myPropInfo = myType.GetProperty(propertyName);
return myPropInfo.GetValue(this, null);
}
set
{
Type myType = typeof(UserConfiguration);
PropertyInfo myPropInfo = myType.GetProperty(propertyName);
myPropInfo.SetValue(this, value, null);
}
}
Then you can get/set properties within the class using
myClassInstance["ClientId"] = myValue;
If I understand what you're asking, perhaps something like this will work for you:
var argDict = arguments.ToDictionary(x => x.PropertyName, x => x.Value);
Members.ClientID = argDict["ClientID"];
...
If you need to do some special comparison on the keys you can provide the dictionary it's own IEqualityComparer. For example, this will make sure that the keys are treated case-insensitively:
var argDict = arguments.ToDictionary(x => x.PropertyName, x => x.Value,
StringComparer.OrdinalIgnoreCase);
This will work fine as long as the arguments list contains all the values you need. If some arguments might be missing, you'd have to do something like this:
if (argDict.ContainsKey("ClientID")) {
Members.ClientID = argDict["ClientID"];
}
Or possibly something like this:
Members.ClientID = argDict.ContainsKey("ClientID") ? argDict["ClientID"] : "DefaultValue";
I think that your basic intent is to set the value of a property on a target object based on the property name. Since you did not provide the Argument class I will assume it is defined like this:
public class Argument
{
public string PropertyName{get; set;}
public object PropertyValue{get;set;}
}
Further assume you have the class Blah defined like this:
public class Blah
{
public string AString{get; set;}
public int AnInt{get; set;}
public DirectoryInfo ADirInfo{get; set;}
}
If you wish to assign to the properties of a Blah object based on the values in List<Argument> you can do so like this:
List<Argument> arguments = new List<Argument>
{
new Argument(){PropertyName = "AString", PropertyValue = "this is a string"},
new Argument(){PropertyName = "AnInt", PropertyValue = 1729},
new Argument(){PropertyName = "ADirInfo", PropertyValue = new DirectoryInfo(#"c:\logs")}
};
Blah b = new Blah();
Type blahType = b.GetType();
foreach(Argument arg in arguments)
{
PropertyInfo prop = blahType.GetProperty(arg.PropertyName);
// If prop == null then GetProperty() couldn't find a property by that name. Either it doesn't exist, it's not public, or it's defined on a parent class
if(prop != null)
{
prop.SetValue(b, arg.PropertyValue);
}
}
This depends on the objects stored in Argument.PropertyValue having the same type as the property of Blah referred to by Argument.PropertyName (or there must be an implicit type conversion available). For example, if you alter the List<Argument> as follows:
List<Argument> arguments = new List<Argument>
{
new Argument(){PropertyName = "AString", PropertyValue = "this is a string"},
new Argument(){PropertyName = "AnInt", PropertyValue = 1729},
new Argument(){PropertyName = "ADirInfo", PropertyValue = "foo"}
};
you will now get an exception when attempting to assign to Blah.ADirInfo: Object of type 'System.String' cannot be converted to type 'System.IO.DirectoryInfo'
I want to get value for a dynamic property of a dynamic object.
Here is my Code..
public string ReturnProperty(object ob, string prop)
{
Type type = ob.GetType();
PropertyInfo pr = type.GetProperty(prop);
//Here pr is null..Dont know whats wrong
return pr.GetValue(ob, null).ToString();
}
My guess is that either it isn't a public property, or you've got the name wrong, or it isn't a property at all (but a public field).
It's impossible to say more without knowing what the actual type is, but that should be a start.
You mention that this is a "dynamic object" but that's not really very descriptive. Bear in mind that the CLR itself doesn't know anything about the DLR - if you mean this is a type which implements IDynamicMetaObjectProvider or extends DynamicObject, then you won't be able to get at the properties with "normal" reflection like this.
In my case ob did not have pr getter setter properly.
//causes GetProperty to return null
public class MyClass{
public object pr;
}
//Works
public class MyClass{
public object pr { get; set; }
}
In my case, I had to define get and set. See post above
public string MyPropertyName { get; set; }
After this I could get the property by:
typeof(MyClassItem).GetProperty("PropertyName")
If the item you are attempting to access doesn't have getter and setter accessors, then most likely it is a field.
So your code would work as follows:
FieldInfo fieldInfo = type.GetField(fieldName);
Try the Type.GetProperty(String, BindingFlags) overload and select the right binding flags.
Example for ExpandoObject(it implements IDynamicMetaObjectProvider Jon Skeet mentioned):
public static string ReturnProperty(object ob, string prop)
{
if (ob is ExpandoObject)
{
return ((ExpandoObject)ob).Single(e => e.Key == prop).Value.ToString();
}
Type type = ob.GetType();
PropertyInfo pr = type.GetProperty(prop);
return pr.GetValue(ob, null).ToString();
}
//--
dynamic dyna = new ExpandoObject();
dyna.Name = "Element";
Console.WriteLine(ReturnProperty(dyna, "Name"));
I had the same error, the problem lies in the field names, if you have a field to read from the sql "isField" and its class has a field is named "IsField". The compiler will read case sensitive , for all reason it's a different field, for that reason you have null. Check yours casesensitive fields nomenculature.
I was trying to access a public property, but I was using BindingFlags.NonPublic instead of BindingFlags.Public.
I tried this & it worked.
public string ReturnProperty(object ob, string prop)
{
Type type = ob.GetType();
PropertyInfo pr = type.GetProperty(prop);
//Here pr is null..Dont know whats wrong
return pr.GetValue(ob, null).ToString();
}
ReturnProperty(new { abc = 10 }, "abc");
Whats wrong???
I just came across this issue when I was passing in the wrong data of a sorted grid view in an MVC project.
public HolidaysGridViewModel()
{
this.Sort = "HolidayDate"; // this was the wrong name
this.SortDir = "ASC";
}
It made me realize after reading your question that you were most likely passing in the name of a business from the database instead of the name of the database column object and therefore no were results were found which may have been the cause of your null value.