How to get inherited property value using reflection?
I try with BindingFlags but still trigger NullReferenceException
object val = targetObject.GetType().GetProperty("position", BindingFlags.FlattenHierarchy).GetValue(targetObject, null);
position is iherited public property and has a declared value.
EDIT:
class myParent
{
public float[] position;
public myParent()
{
this.position = new float[] { 1, 2, 3 };
}
}
class myChild : myParent
{
public myChild() : base() { }
}
myChild obj = new myChild();
PropertyInfo p = obj.GetType().GetProperty("position", BindingFlags.Instance | BindingFlags.Public);
I tried with several combinations with BindingFlags but p always is null :( ,
If you use the overload with BindingFlags you have to explicitly specify all the flags what you are interested.
Also note that: (from MSDN)
You must specify either BindingFlags.Instance or BindingFlags.Static
in order to get a return.
object val = targetObject.GetType()
.GetProperty("position",
BindingFlags.FlattenHierarchy |
BindingFlags.Instance |
BindingFlags.Public)
.GetValue(targetObject, null);
EDIT:
You have a position field not a property !.
(A good place to start learning the difference: Difference between Property and Field in C# 3.0+ especially this answer)
Change your position to a property:
public float[] position { get; set; }
Or you use the targetObject.GetType().GetField(... method to retrieve the field.
BindingFlags.FlattenHierarchy
works only for static members. Be sure to specify
BindingFlags.Instance | BindingFlags.Public
and you should get inherited properties.
Related
EDIT: I had an error in my unit test. So my question is invalid. I have marked the correct answer below and the question should be "How do I get private members on inherited classes".
I traverse the class hierarchy to get the private members I need.
I am using reflection to get the members of a class hierarchy. Originally, I was pulling properties and fields separately. I refactored my code just to get MemberInfo and started running into a small issue.
Originally I was able to get all the fields using:
type.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic)
This would return all public and private fields in the class hierarchy.
For example on the classes:
class ViewModel {
[MetaData("MetaFieldType1", typeof(Parent), null)]
public string field1;
[MetaData("MetaFieldType2", typeof(Parent), "Field Value 2")]
private string field2;
[MetaData("MetaProtectedField", typeof(Parent), "Protected Field")]
protected string pField;
protected string pField2;
[MetaData("MetaType 1", typeof(Parent), null)]
public String Prop1 { get; set; }
[MetaData("MetaType 2", typeof(Parent), "The Value")]
public String Prop2 { get; set; }
public string Field2 { get { return field2; } set { field2 = value; } }
}
class ViewModel2 : ViewModel {
public string PField { get { return pField; } set { pField = value; } }
[MetaData("MetaProtectedField2", typeof(Parent), null)]
public string PField2 { get { return pField2; } set { pField2 = value; } }
}
4 fields would be returned on both ViewModel and ViewModel2
I then changed the code to use (rather than getting properties then fields):
type.FindMembers(MemberTypes.Field | MemberTypes.Property,
BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic,
allMemberFilter,null)
Where
protected static readonly MemberFilter allMemberFilter = new MemberFilter(
delegate (MemberInfo objMemberInfo, Object objSearch) {
return true;
});
and ViewModel returns all the properties and fields; however, ViewModel2 is missing the private fields (i.e. field2) found in ViewModel. This was not the case when getting them using the GetFields and GetProperties with the same binding flags.
If I change field2 to protected, then it shows up in the search.
Is this an issue with the framework or am I missing something?
First of all, private members of a base Type are considered members of the inherited Type. from C# Language Specification Version 5.0:
A class inherits the members of its base class. Inheritance means that a class implicitly contains all members of its base class (A.k.a: Including the private members of the base class), except for the instance and static constructors, and the destructors of the base class.
If you inspect the BindingFlags.FlattenHierarchy documentation, you will see it refers only to static members:
Specifies that public and protected static members up the hierarchy should be returned.
With that said, apparently, and unfortunately the only way to get private members of a Type is using reflection on their declaring Type, which means, in our case - the base Type itself.
So we will have to write an extension to do so recursively:
static class TypeExtensions
{
public static IEnumerable<MemberInfo> GetAllInstanceMembers(this Type type)
{
if (type == null || type == typeof (object) || !type.IsClass || type.IsInterface)
{
return Enumerable.Empty<MemberInfo>();
}
IEnumerable<MemberInfo> baseMembers = type.BaseType.GetAllInstanceMembers();
IEnumerable<MemberInfo> interfacesMembers = type.GetInterfaces().SelectMany(x => x.GetAllInstanceMembers());
IEnumerable<MemberInfo> currentMembers = type.FindMembers(MemberTypes.Field | MemberTypes.Property,
BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic,
AllMemberFilter, null);
return baseMembers.Concat(interfacesMembers).Concat(currentMembers);
}
static readonly MemberFilter AllMemberFilter = (objMemberInfo, objSearch) => true;
}
Note that apparently BindingFlags.DeclaredOnly makes the method return the backing fields generated by the constructor for the auto-properties while in the question's example they are not returned, this can be fixed by changing the return line to:
return baseMembers.Concat(interfacesMembers).Concat(currentMembers)
.Where(x => x.GetCustomAttribute<CompilerGeneratedAttribute>() == null);
Also, I must say that I am getting the same results for GetMembers, FindMembers and GetProperties/GetFields
I have a class something like below:
Class A : B<C>
{
public A(C entity):base(entity)
{}
}
abstract class B<T>
{
public B(T entity)
{
Entity = entity;
}
public T Entity { get; private set; }
}
Class C: D
{
public string prop2{get;set;}
}
Class D
{
public string prop1{get;set;}
}
Main()
{
A obj = new A(new C());
obj.GetType().GetProperty("prop1", BindingsFlag.Instance|BindingsFlag.FlatteredHierarchy)// is null
}
I have object of class A.
I want to get property value from this object at runtime.
I am trying with
obj.GetType().GetProprty("propertyName",
BindingsFlag.FlattenHierarchy).GetValue(obj, null);
However GetProprty() is returing null as that property is declared either in D or C class.
Can someone please suggest me how to achieve this?
Thanks in advance.
GetType().GetProperty("propertyName", BindingsFlag.FlattenHierarchy)
.GetValue(obj, null);
You are misssing binding flag that specifies wheter get instance or static property:
BindingsFlag.FlattenHierarchy | BindingsFlag.Instance
According to MSDN flag BindingsFlag.Instance or BindingsFlag.Static must be specifed explicity in order to get not null values:
You must specify either BindingFlags.Instance or BindingFlags.Static
in order to get a return.
What's more, public properties are exlcuded by default. So if you property is public you need to specify additional flag:
BindingsFlag.FlattenHierarchy | BindingsFlag.Instance | BindingsFlag.Public
Remarks:
Specify BindingFlags.Public to include public properties in the
search.
If property in base is private, FlattenHierarchy will not enumerate it:
(...) private static members in inherited classes are not included
If this is your case, I am afraid that you have to manually travel through base class and search for that property.
Make also sure, that property name is valid and exists.
EDIT:
After your edit, I see the problem. Your class A is not sub-class of D class (you want to get property from D class). That is why getting property value is not working in such way.
You need to follow the following steps:
// get entity prop value
var entityValue =
(obj.GetType()
.GetProperty("Entity",
BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public)
.GetValue(obj));
// get prop value
var prop1Value =
entityValue.GetType()
.GetProperty("prop1",
BindingFlags.FlattenHierarchy |
BindingFlags.Instance |
BindingFlags.Public)
.GetValue(entityValue);
Remember to handle null values etc.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I have the following classes
public class Car
{
public int CarId { get; set;}
public string Description { get; set;}
public Engine Engine { get; set;}
}
public class Engine
{
public int EngineId { get; set;}
public int Description { get; set;}
}
Now i want to iterate all the properties in Car and all the properties in Engine, i dont want to hardcode the property names "Car or Engine"
Example to get all properties of Car, where obj is instance of Car.
var properties = obj.GetType().GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public);
But this doesnt iterate the properties of Engine.
FlattenHierarchy does not do what you think it does, and instead follows the inheritance hiearchy for static members.
If you'd like to get sub-properties for objects, you'll need to do that yourself:
static IEnumerable<PropertyInfo> FlattenProperties(Type type)
{
// Assumption #1: you do not want "simple" types enumerated
if (!type.IsClass)
return Enumerable.Empty<PropertyInfo>();
// Assumption #2: you want to ignore "the usual suspects"
if (type.Namespace != null && type.Namespace.StartsWith("System"))
return Enumerable.Empty<PropertyInfo>();
// Assumption #3: your class hierarchy won't destroy recursion
// Assumption #4: you simply want the PropertyInfo
return type.GetProperties(BindingFlags.FlattenHierarchy
| BindingFlags.Instance
| BindingFlags.Public)
.SelectMany(pi => new[] { pi }
.Concat(FlattenProperties(pi.PropertyType)));
}
If this is used in code where you (a) know the depth of the recursion, and (b) have the means to alter the code, I'd suggest creating either a base class, interface, or attribute for these types/properties.
// Replace Assumptions #1 and #2 above with this:
// Assumption #5: given interface ISomething { }
if (!typeof(ISomething).IsAssignableFrom(type))
return Enumerable.Empty<PropertyInfo>();
If you need the "property tree" (i.e. Assumption #4 is incorrect):
static IEnumerable<IEnumerable<PropertyInfo>> FlattenProperties(
Type type,
IEnumerable<PropertyInfo> ancestors = null)
{
// change to Assumptions #1/#2 or #5 to yield break
// ...
ancestors = ancestors ?? Enumerable.Empty<PropertyInfo>();
var properties = type.GetProperties(
BindingFlags.FlattenHierarchy
| BindingFlags.Instance
| BindingFlags.Public);
foreach (var property in properties)
{
// again, Assumption #3: your class hierarchy won't destroy recursion
// Assumption #6: you actually want the initial nested property too
yield return ancestors.Concat(new[] { property });
foreach (var nested in FlattenProperties(
property.PropertyType,
ancestors.Concat(new [] { property })))
{
yield return nested;
}
}
}
Which, in the second case, produces output similar to:
// foreach (var tree in FlattenProperties(typeof(Car)))
// {
// Console.WriteLine("{0}", String.Join(".", tree.Select(pi => pi.Name)));
// }
CarId
Description
Engine
Engine.EngineId
Engine.Description
Try this. You've to repeat the same for all properties of a type to get nested properties.
var properties = obj.GetType().GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public);
foreach(var pi in properties)
{
var nestedProperties = pi.PropertyType.GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public);
}
You can just iterate through the properties of each property's PropertyType, just as you've done to get the first-level properties.
Here's a quick and dirty example using Linq:
var properties =
from p1 in obj.GetType().GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public)
from p2 in p1.PropertyType.GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public).DefaultIfEmpty()
select new { OuterProperty = p1, InnerProperty = p2 };
foreach(var prop in properties)
{
Console.WriteLine(prop.OuterProperty.Name + (prop.InnerProperty != null ? "." + prop.InnerProperty.Name : ""));
}
Produces the output:
CarId
Description.Chars
Description.Length
Engine.EngineId
Engine.Description
You might want to only evaluate classes in a given namespace (so you don't end up capturing the Length property of Description, for example):
var properties =
from p1 in obj.GetType().GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public)
from p2 in p1.PropertyType.Namespace == "MyNamespace"
? p1.PropertyType.GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public).DefaultIfEmpty()
: new PropertyInfo[] { null }
select new { OuterProperty = p1, InnerProperty = p2 };
Produces the output:
CarId
Description
Engine.EngineId
Engine.Description
Or perhaps more elegantly if you define a certain attribute to mark the properties you want to traverse:
[AttributeUsage(AttributeTargets.Property)]
public class TraversableAttribute: Attribute { }
public class Car
{
public int CarId { get; set;}
public string Description { get; set;}
[Traversable]
public Engine Engine { get; set;}
}
...
var properties =
from p1 in obj.GetType().GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public)
from p2 in p1.GetCustomAttributes(typeof(TraversableAttribute), true).Length > 0
? p1.PropertyType.GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public).DefaultIfEmpty()
: new PropertyInfo[] { null }
select new { OuterProperty = p1, InnerProperty = p2 };
This will produce the same output as the previous example.
If you only need the properties from the original class and its 1st lower hierarchy level, then you could just create the following attribute like so.
public class BrowsableAttribute : Attribute { }
Now simply decorate the classes you want to browse for properties, in your case, that would be the Engine class.
[Browsable]
class Engine
{
public int EngineId { get; set; }
public int Description { get; set; }
}
Now all you need to do is use the following extension method.
public static class TypeExtensions
{
public static void BrowseProperties(this Type type)
{
var h1 = typeof(Car).GetProperties().ToList();
var h2 = h1.Where(x => x.PropertyType.GetCustomAttributes(true).OfType<BrowsableAttribute>().Any());
h2.ToList().ForEach(x => h1.AddRange(x.PropertyType.GetProperties()));
h1.ForEach(x => Console.WriteLine(x.Name));
}
}
Which will produce the desired result. To see this, execute the following lines of code.
class Program
{
static void Main()
{
typeof(Engine).BrowseProperties();
Console.Read();
}
}
I'm trying to read all of the properties of a given object, reading in only those that are declared on the object's type, excluding those that are inherited. IE:
class Parent {
public string A { get; set; }
}
class Child : Parent {
public string B { get; set; }
}
And so I want to only get B back. Reading the docs, I assumed below was what I needed, but that actually returned nothing at all.
var names = InstanceOfChild.GetType().GetProperties(BindingFlags.DeclaredOnly).Select(pi => pi.Name).ToList();
Just need a couple other BindingFlags
var names = InstanceOfChild.GetType().GetProperties(
BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.Instance).Select(pi => pi.Name).ToList();
Try this:
var names = InstanceOfChild.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly).Select(pi => pi.Name).ToList();
I added the BidningFlags.Instance and BindingFlags.Public to the search parameters which according to the MSDN documentation respectfully:
Specifies that instance members are to
be included in the search.
and
Specifies that public members are to
be included in the search.
I'm trying to use reflection to get a property from a class. Here is some sample code of what I'm seeing:
using System.Reflection;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
PropertyInfo[] tmp2 = typeof(TestClass).GetProperties();
PropertyInfo test = typeof(TestClass).GetProperty(
"TestProp", BindingFlags.Public | BindingFlags.NonPublic);
}
}
public class TestClass
{
public Int32 TestProp
{
get;
set;
}
}
}
When I trace through this, this is what I see:
When I fetch all properties using GetProperties(), the resulting array has one entry, for property TestProp.
When I try to fetch TestProp using GetProperty(), I get null back.
I'm a little stumped; I haven't been able to find anything in the MSDN regarding GetProperty() to explain this result to me. Any help?
EDIT:
If I add BindingFlags.Instance to the GetProperties() call, no properties are found, period. This is more consistent, and leads me to believe that TestProp is not considered an instance property for some reason.
Why would that be? What do I need to do to the class for this property to be considered an instance property?
Add BindingFlags.Instance to the GetProperty call.
EDIT: In response to comment...
The following code returns the property.
Note: It's a good idea to actually make your property do something before you try to retrieve it (VS2005) :)
using System.Reflection;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
PropertyInfo[] tmp2 = typeof(TestClass).GetProperties();
PropertyInfo test = typeof(TestClass).GetProperty(
"TestProp",
BindingFlags.Instance | BindingFlags.Public |
BindingFlags.NonPublic);
Console.WriteLine(test.Name);
}
}
public class TestClass
{
public Int32 TestProp
{
get
{
return 0;
}
set
{
}
}
}
}
Try to add the following tag:
System.Reflection.BindingFlags.Instance
EDIT: This works (at least to me)
PropertyInfo test = typeof(TestClass).GetProperty("TestProp", BindingFlags.Public | BindingFlags.Instance);
Console.WriteLine(test.Name);
You need to specify whether it is static or an instance (or both) too.