I have this method and want to get all properties from the FieldInfos? How to get it?
private static void FindFields(ICollection<FieldInfo> fields, Type t)
{
var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly;
foreach (var field in t.GetFields(flags))
{
fields.Add(field);
}
var baseType = t.BaseType;
if (baseType != null)
{
FindFields(fields, baseType);
}
}
var fields = new Collection<FieldInfo>();
FindFields(fields, this.GetType());
Thanks.
Best regards.
To get the value of a field for a specific object use GetValue and pass the object for which you want to get the value.
var fields = new Collection<FieldInfo>();
FindFields(fields, this.GetType());
foreach (var field in fields)
{
Console.WriteLine( "{0} = {1}", field.Name , field.GetValue(this));
}
Related
public abstract class a
{
public static string Description { get; protected set; }
}
public class b : a
{
static b()
{
Description = "asdf";
}
}
I want to access b.description with reflection (and its value would be "asdf") but find any good solution.
Using Reflection, you have to specify ALL the flags you want.
Here is the sample.
private void button1_Click( object sender, EventArgs e )
{
b obj = new b();
var props = obj.GetType().GetProperties( BindingFlags.Public );
var prop = obj.GetType().GetProperty( "Description",
BindingFlags.Static |
BindingFlags.FlattenHierarchy |
BindingFlags.Public
);
MessageBox.Show( prop.GetValue( obj, null ).ToString() );
}
However if you elaborate on your use case, there may be better solution than reflection.
You can do it in this way:
// Get a PropertyInfo of specific property type(T).GetProperty(....)
PropertyInfo propertyInfo;
propertyInfo = typeof(b)
.GetProperty("Description", BindingFlags.Public | BindingFlags.Static| BindingFlags.FlattenHierarchy);
// Use the PropertyInfo to retrieve the value from the type by not passing in an instance
object value = propertyInfo.GetValue(new b(), null);
value is description value:
asdf
For more information https://learn.microsoft.com/en-us/dotnet/api/system.reflection.methodbase.invoke?redirectedfrom=MSDN&view=netframework-4.8#System_Reflection_MethodBase_Invoke_System_Object_System_Object___
I did write a model generator to create an interface between client ViewModels and server Models.
Some model classes are serialize with [JsonObject(MemberSerialization.OptIn)] attribute and their properties are marked with [JsonProperty ...] to be serialized, in addition i have some custom attributes which need to be handled in my custom DefaultContractResolver.
Here is how I create my ModelView from any source type
public Type CreateModelView(Type sourceType) {
...
foreach (var attribute in sourceType.CustomAttributes)
typeBuilder.SetCustomAttribute(
new CustomAttributeBuilder(
attribute.Constructor,
attribute.ConstructorArguments.Select(x => x.Value).ToArray()));
...
var fields = GetFields(sourceType);
foreach (var field in fields)
CreateProperty(typeBuilder, field.Name, field.PropertyType, field.GetCustomAttributes(true));
return typeBuilder.CreateTypeInfo().AsType();
}
private void CreateProperty(TypeBuilder typeBuilder, string name, Type type, object[] customAttributes)
{
var fieldBuilder = typeBuilder.DefineField(name, type, FieldAttributes.Public);
var propertyBuilder = typeBuilder.DefineProperty(name, PropertyAttributes.HasDefault, type, null);
var GetMethodBuilder = typeBuilder.DefineMethod(
"Get" + name,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig, type,
Type.EmptyTypes);
var iLGenerator = GetMethodBuilder.GetILGenerator();
iLGenerator.Emit(OpCodes.Ldarg_0);
iLGenerator.Emit(OpCodes.Ldfld, fieldBuilder);
iLGenerator.Emit(OpCodes.Ret);
var SetMethodBuilder =
typeBuilder.DefineMethod("Set" + name,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
null,
new[] { type });
ILGenerator setIl = SetMethodBuilder.GetILGenerator();
Label modifyProperty = setIl.DefineLabel();
Label exitSet = setIl.DefineLabel();
setIl.MarkLabel(modifyProperty);
setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldarg_1);
setIl.Emit(OpCodes.Stfld, fieldBuilder);
setIl.Emit(OpCodes.Nop);
setIl.MarkLabel(exitSet);
setIl.Emit(OpCodes.Ret);
foreach (var customAttribute in customAttributes)
AddCustomAttributeToProperty(customAttribute, propertyBuilder);
propertyBuilder.SetGetMethod(GetMethodBuilder);
propertyBuilder.SetSetMethod(SetMethodBuilder);
}
/// <summary>
/// Given a custom attribute and property builder, adds an instance of custom attribute
/// to the property builder
/// </summary>
void AddCustomAttributeToProperty(object customAttribute, PropertyBuilder propBuilder)
{
var customAttributeBuilder = BuildCustomAttribute(customAttribute);
if (customAttributeBuilder != null)
{
propBuilder.SetCustomAttribute(customAttributeBuilder);
}
}
static CustomAttributeBuilder BuildCustomAttribute(object customAttribute)
{
ConstructorInfo longestCtor = null;
// Get constructor with the largest number of parameters
foreach (var cInfo in customAttribute.GetType().GetConstructors().
Where(cInfo => longestCtor == null || longestCtor.GetParameters().Length < cInfo.GetParameters().Length))
longestCtor = cInfo;
if (longestCtor == null)
{
return null;
}
// For each constructor parameter, get corresponding (by name similarity) property and get its value
var args = new object[longestCtor.GetParameters().Length];
var position = 0;
foreach (var consParamInfo in longestCtor.GetParameters())
{
var attrPropInfo = customAttribute.GetType().GetProperty(consParamInfo.Name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
if (attrPropInfo != null)
{
args[position] = attrPropInfo.GetValue(customAttribute, null);
}
else
{
args[position] = null;
var attrFieldInfo = customAttribute.GetType().GetField(consParamInfo.Name, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase);
if (attrFieldInfo == null)
{
if (consParamInfo.ParameterType.IsValueType)
{
args[position] = Activator.CreateInstance(consParamInfo.ParameterType);
}
}
else
{
args[position] = attrFieldInfo.GetValue(customAttribute);
}
}
++position;
}
var propList = new List<PropertyInfo>();
var propValueList = new List<object>();
foreach (var attrPropInfo in customAttribute.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (!attrPropInfo.CanWrite)
{
continue;
}
object defaultValue = null;
var defaultAttributes = attrPropInfo.GetCustomAttributes(typeof(DefaultValueAttribute), true);
if (defaultAttributes.Length > 0)
{
defaultValue = ((DefaultValueAttribute)defaultAttributes[0]).Value;
}
var value = attrPropInfo.GetValue(customAttribute, null);
if (value == defaultValue)
{
continue;
}
propList.Add(attrPropInfo);
propValueList.Add(value);
}
return new CustomAttributeBuilder(longestCtor, args, propList.ToArray(), propValueList.ToArray());
}
i tested this code as well with field.CustomAttributes and result was same.
propertyBuilder.SetCustomAttribute(
new CustomAttributeBuilder(
attribute.Constructor,
attribute.ConstructorArguments.Select(x => x.Value).ToArray()));
and the problem is here in ContractResolver when the result of attributes is empty,
public class ContractResolver : DefaultContractResolver {
...
protected override IList<JsonProperty> CreateProperties(
Type type,
MemberSerialization memberSerialization)
{
var propertyList = base.CreateProperties(type, memberSerialization);
foreach (var jProperty in propertyList)
{
var attributes = jProperty.AttributeProvider.GetAttributes(true);
var isBindNever = attributes.OfType<BindNeverAttribute>().Any();
var isKey = attributes.OfType<KeyAttribute>().Any();
...
}
and [JsonProperty ...] attributes are not working as well.
P.S. I can get custom attributes from this code
var PropertyType = jProperty.DeclaringType.GetProperties()
.Where(property => property.Name.ToLower() == jProperty.PropertyName.ToLower())
.FirstOrDefault();
The problem is why is jProperty.AttributeProvider.GetAttributes not working as expected,
Why [JsonProperty] not working as expected (marked properties won't serialized).
This will solve problem, however it's not efficient
public class JsonResolver : DefaultContractResolver
{
...
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
var serializableMembers = base.GetSerializableMembers(objectType).Select(memberInfo => memberInfo.Name);
return objectType.GetProperties().Where(memberInfo => serializableMembers.Contains(memberInfo.Name)).Cast<MemberInfo>().ToList();
}
...
}
I have a LookUpEdit control and I need set property value to NullText with reflection, but I'm getting the TargetException:
private static void SetObjectProperty(string propiedad, string valor, object obj)
{
if (obj.GetType() == typeof(LookUpEdit))
{
string[] vv = propiedad.Split('.');
string prop = vv[0];
string propType = vv[1];
var p = obj.GetType().GetProperty(prop, BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
PropertyInfo propertyInfo = p.PropertyType.GetProperty(propType);
if (propertyInfo != null)
{
propertyInfo.SetValue(obj, valor, null);
}
}
}
I only get the exception with LookUpEdit control.
"propiedad" is a string contains "Properties.NullText" so this is why I'm doing a split
You should apply operations with nested properties to corresponding nested objects:
static void SetObjectProperty(object obj, string propertyPath, object value) {
if(obj != null && obj.GetType() == typeof(LookUpEdit)) {
string[] parts = propertyPath.Split('.');
var rootInfo = typeof(LookUpEdit).GetProperty(parts[0],
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
object root = rootInfo.GetValue(obj); // obtaining a root
var nestedInfo = rootInfo.PropertyType.GetProperty(parts[1]);
if(nestedInfo != null)
nestedInfo.SetValue(root, value, null); // using root object
}
}
PS. Why you're using this ugly way of modifying object properties?
I'm trying to make this code in IL using ILGenerator.Emit
class Foo
{
...
}
public class TestClass
{
public static void test()
{
Type t = typeof(Foo);
foreach(PropertyInfo p in t.GetProperties())
{
GenerateVar(p.PropertyInfo);
}
}
public static object GenerateVar(Type var)
{
if (var == typeof(Int32))
{
return 5;
}
else if (var == typeof(Char))
{
return 'a';
}
return null;
}
}
This is what I've done so far, and had some complications:
MethodInfo mi = TestClass.GetType().GetMethod("GenerateVar",
BindingFlags.Public |
BindingFlags.Instance);
ILGenerator generator = mb.GetILGenerator();
LocalBuilder propertyType;
LocalBuilder TestClass = mb_gen.DeclareLocal(typeof(TestClass));
foreach (PropertyInfo pi in t.GetProperties())
{
propertyType = mb_gen.DeclareLocal(pi.PropertyType);
//loads into the stack the current PropertyType and the method class
generator.Emit(OpCodes.Ldloc, TestClass);
generator.Emit(OpCodes.LdLoc, propertyType);
//calls GenerateVar(Type var) to get a PropertyType var
generator.Emit(OpCodes.Callvirt, mi);
}
It gives me the following exception:
-> expected type: System.Type , Received type: System.String
System.String is the property type that was given by: pi.PropertyType;
What am I doing wrong?
Thanks in advance
As thehennyy comment, if you can give us the full code we will can to help better. I'm trying to help here because I guess what you try to do.
So I based here on your C# code. As I understand you want to create a method that get properties of type (Foo in your case) and foreach of them, get some value based on type.
Here is a snippet of doing it for first property of the type. To complete the code you need to emit the loop, or, like you wrote in your question, loop on the properties in your C# code and emit the code for each property one after one.
static void CallGenerateVar()
{
var dm = new DynamicMethod("CallGenerateVar", typeof(object), Type.EmptyTypes, typeof(TestClass));
MethodInfo generateVarMethod = typeof(TestClass).GetMethod("GenerateVar", BindingFlags.Public | BindingFlags.Instance);
var ilGen = dm.GetILGenerator();
var properties = ilGen.DeclareLocal(typeof(PropertyInfo[]));
var index = ilGen.DeclareLocal(typeof(int));
var propInfo = ilGen.DeclareLocal(typeof(PropertyInfo));
ilGen.Emit(System.Reflection.Emit.OpCodes.Ldtoken, typeof(Foo));
ilGen.Emit(System.Reflection.Emit.OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", BindingFlags.Static | BindingFlags.Public));
ilGen.Emit(System.Reflection.Emit.OpCodes.Callvirt, typeof(Type).GetMethod("GetProperties", Type.EmptyTypes));
ilGen.Emit(System.Reflection.Emit.OpCodes.Stloc_0);
ilGen.Emit(System.Reflection.Emit.OpCodes.Ldc_I4_0);
ilGen.Emit(System.Reflection.Emit.OpCodes.Stloc_1);
ilGen.Emit(System.Reflection.Emit.OpCodes.Ldloc_0);
ilGen.Emit(System.Reflection.Emit.OpCodes.Ldloc_1);
ilGen.Emit(System.Reflection.Emit.OpCodes.Ldelem_Ref);
ilGen.Emit(System.Reflection.Emit.OpCodes.Stloc_2);
ilGen.Emit(System.Reflection.Emit.OpCodes.Ldloc_0);
ilGen.Emit(System.Reflection.Emit.OpCodes.Ldloc_2);
ilGen.Emit(System.Reflection.Emit.OpCodes.Callvirt, typeof(PropertyInfo).GetMethod("get_PropertyType", BindingFlags.Instance | BindingFlags.Public));
ilGen.Emit(System.Reflection.Emit.OpCodes.Callvirt, generateVarMethod);
ilGen.Emit(System.Reflection.Emit.OpCodes.Ret);
var del = (Func<object>)dm.CreateDelegate(typeof(Func<object>));
var result = del.Invoke();
}
In case our Foo type looks like this:
class Foo
{
public int MyProperty { get; set; }
}
And GenerateVar looks like this:
public object GenerateVar(Type var)
{
if (var == typeof(Int32))
{
return 5;
}
else if (var == typeof(Char))
{
return 'a';
}
return null;
}
It will print 5
I am trying to set a value to a Nested Property of Class dynamically using reflection. Could anyone help me to do this.
I am having a class Region like below.
public class Region
{
public int id;
public string name;
public Country CountryInfo;
}
public class Country
{
public int id;
public string name;
}
I have a Oracle Data reader to provide the Values from the Ref cursor.
which will give me as
Id,name,Country_id,Country_name
I could able to assign the values to the Region.Id, Region.Name by below.
FieldName="id"
prop = objItem.GetType().GetProperty(FieldName, BindingFlags.Public | BindingFlags.Instance);
prop.SetValue(objItem, Utility.ToLong(reader_new[ResultName]), null);
And for the Nested Property I could able to do the assign values to the as below by creating a Instance by reading the Fieldname.
FieldName="CountryInfo.id"
if (FieldName.Contains('.'))
{
Object NestedObject = objItem.GetType().GetProperty(Utility.Trim(FieldName.Split('.')[0]), BindingFlags.Public | BindingFlags.Instance);
//getting the Type of NestedObject
Type NestedObjectType = NestedObject.GetType();
//Creating Instance
Object Nested = Activator.CreateInstance(typeNew);
//Getting the nested Property
PropertyInfo nestedpropinfo = objItem.GetType().GetProperty(Utility.Trim(FieldName.Split('.')[0]), BindingFlags.Public | BindingFlags.Instance);
PropertyInfo[] nestedpropertyInfoArray = nestedpropinfo.PropertyType.GetProperties();
prop = nestedpropertyInfoArray.Where(p => p.Name == Utility.Trim(FieldName.Split('.')[1])).SingleOrDefault();
prop.SetValue(Nested, Utility.ToLong(reader_new[ResultName]), null);
Nestedprop = objItem.GetType().GetProperty(Utility.Trim(FieldName.Split('.')[0]), BindingFlags.Public | BindingFlags.Instance);
Nestedprop.SetValue(objItem, Nested, null);
}
The above assign values to Country.Id.
But Since I am creating instance each and every time I could not able to get the previous Country.Id value if I go for the Next Country.Name.
Could anybody tell could to assign values to the objItem(that is Region).Country.Id and objItem.Country.Name. Which means how to assign values to the Nested Properties instead of creating instance and assigning everytime.
Thanks in advance.!
You should be calling PropertyInfo.GetValue using the Country property to get the country, then PropertyInfo.SetValue using the Id property to set the ID on the country.
So something like this:
public void SetProperty(string compoundProperty, object target, object value)
{
string[] bits = compoundProperty.Split('.');
for (int i = 0; i < bits.Length - 1; i++)
{
PropertyInfo propertyToGet = target.GetType().GetProperty(bits[i]);
target = propertyToGet.GetValue(target, null);
}
PropertyInfo propertyToSet = target.GetType().GetProperty(bits.Last());
propertyToSet.SetValue(target, value, null);
}
Get Nest properties e.g., Developer.Project.Name
private static System.Reflection.PropertyInfo GetProperty(object t, string PropertName)
{
if (t.GetType().GetProperties().Count(p => p.Name == PropertName.Split('.')[0]) == 0)
throw new ArgumentNullException(string.Format("Property {0}, is not exists in object {1}", PropertName, t.ToString()));
if (PropertName.Split('.').Length == 1)
return t.GetType().GetProperty(PropertName);
else
return GetProperty(t.GetType().GetProperty(PropertName.Split('.')[0]).GetValue(t, null), PropertName.Split('.')[1]);
}
Expanding on Jon Skeets answer, if the value you're getting is null and you want to create an object for that type:
public void SetProperty(string compoundProperty, object target, object value)
{
var bits = compoundProperty.Split('.');
for (var i = 0; i < bits.Length - 1; i++) {
var propertyToGet = target.GetType().GetProperty(bits[i]);
var propertyValue = propertyToGet.GetValue(target, null);
if (propertyValue == null) {
propertyValue = Activator.CreateInstance(propertyToGet.PropertyType);
propertyToGet.SetValue(target, propertyValue);
}
target = propertyToGet.GetValue(target, null);
}
var propertyToSet = target.GetType().GetProperty(bits.Last());
propertyToSet.SetValue(target, value, null);
}
Expanding on Luke Garrigan Answer:
If you want to have it working also with non lists too:
public void SetProperty(string compoundProperty, object target, object value)
{
var bits = compoundProperty.Split('.');
if(bits == null) bits = new string[1]{compoundProperty};
for (var i = 0; i < bits.Length - 1; i++) {
var propertyToGet = target.GetType().GetProperty(bits[i]);
var propertyValue = propertyToGet.GetValue(target, null);
if (propertyValue == null) {
propertyValue = Activator.CreateInstance(propertyToGet.PropertyType);
propertyToGet.SetValue(target, propertyValue);
}
target = propertyToGet.GetValue(target, null);
}
var propertyToSet = target.GetType().GetProperty(bits.Last());
propertyToSet.SetValue(target, value, null);
}