I have this very simple test because the full version doesn't work either;
public class dfd
{
public string g { get; set; }
}
and then;
Type myType = typeof(dfd);
FieldInfo[] b = myType.GetFields(BindingFlags.Public);
When I look at b there is no field info.
{System.Reflection.FieldInfo[0]}
Any ideas?
You have an automatic public property, which defines a private field. If you ask for the non-public fields, you'll get the backing field of that property.
BTW, you need to ask for both BindingFlags.NonPublic | BindingFlags.Instance, otherwise you won't retrieve that field.
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 base class CrmObject and a few classes that inherit it (Aufgabe, Kontakt, ...). I only have a string-value for the child-class and I want to get both the Properties of CrmObject and the specific child-class in one Statement.
I'd get the properties of the child-class like this:
var propertyInfos = typeof(CrmObject).Assembly.GetType("Aufgabe").GetProperties().Where(p => Attribute.IsDefined(p, typeof(ImportParameter)));
But I'd like to get the Properties of CrmObject, too. Possibly within the same statement.
[UPDATE]
This should be it. I'll test it later. Thank you, guys. :)
var propertyInfos = typeof(CrmObject).Assembly.GetType("DAKCrmImport.Core." +crmType).GetProperties().Where(p => Attribute.IsDefined(p, typeof(ImportParameter)));
Weirdly enough you don't need the bindingflag parameter to flatten the hierarchy. Apparently it's the default value? Well .. whatever. It works :)
Works fine for me.
public class CrmObject
{
[ImportParameter]
public string Name { get; set; }
}
public class Aufgabe : CrmObject
{
[ImportParameter]
public int Id { get; set; }
}
public class ImportParameterAttribute : Attribute
{
}
public class InheritenceProgram
{
public static void Main()
{
var propertyInfos = typeof(CrmObject).Assembly.GetType("Aufgabe").GetProperties().Where(p => Attribute.IsDefined(p, typeof(ImportParameterAttribute)));
var list = propertyInfos.ToList();
}
}
As Mark said, you need to specify BindingFlags.FlattenHierarchy.
But when you specify the BindingFlags, you need to specify exactly what you want. That means that you also have to specify BindingFlags.Instance to get the instance members and BindingFlags.Public to include public members.
So the command would look like this:
var propertyInfos = typeof(CrmObject).Assembly.GetType(crmType).GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public).Where(p => Attribute.IsDefined(p, typeof(ImportParameter)));
You can read more about it in this great SO answer and in the documentation of Type.GetProperties.
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.
One class has a field. Second class should be able to reference that field and to change it. Since you cannot get the address of a field in memory, I have to use reflections. But my approach only works on non-encapsulated fields. So basically:
This works:
public class Dummy
{
public int field;
public Dummy(int value)
{
this.field = value;
}
}
class Program
{
public static void Main()
{
Dummy d = new Dummy(20);
//Shows 20
Console.WriteLine(d.field.ToString());
d.GetType().GetField("field", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).SetValue(d, 40);
//It should show 40 now
Console.WriteLine(d.field.ToString());
}
}
This doesn't (throws NullReferenceException):
public class Dummy
{
public int field { get; set; }
public Dummy(int value)
{
this.field = value;
}
}
class Program
{
public static void Main()
{
Dummy d = new Dummy(20);
//Shows 20
Console.WriteLine(d.field.ToString());
d.GetType().GetField("field", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).SetValue(d, 40);
//It should show 40 now
Console.WriteLine(d.field.ToString());
}
}
Why? How do I fix it? Can I even access an encapsulated object like that? Thank you!
It is not a field any longer. Instead of GetField you should use GetProperty method:
d.GetType().GetProperty(...);
In both cases, you could (and should) write
d.field = 40;
When you want a field to be non-private, you should always make it a property, as in your second example. You should never have public fields. Note that if you need to access a property via reflection, you'd use GetProperty, not GetField.
Can you be more precise about the use case?
Maybe an interface that isolates the properties' accessors is better:
public interface IDummy
{
int field { get; set; }
}
You'll be able to manipulate the property without knowing anything about the rest of the object. Isn't that what you need when you want to "reference" a single field?
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.