I created some custom attribute to apply it on a class member:
[
System.AttributeUsage(
AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true
)
]
internal class ActionAttribute : Attribute
{
private Action action;
public ActionAttribute(Action action)
{
this.action = action;
}
public Action getThis()
{
return this.action;
}
}
But am struggling on how to retrieve it's value using reflection.
This is my attempt:
public static Device Serialize(string deviceName, Dictionary<string, dynamic> fields)
{
var itce = devices[deviceName];
Type objectType = itce.GetType();
MemberInfo[] fieldsInfo = objectType.GetMembers();
foreach (var field in fieldsInfo.Where(p => p.MemberType == MemberTypes.Property))
{
Console.WriteLine(field.Name);
object[] actionAttributes = field.GetCustomAttributes(typeof(ActionAttribute), false);
foreach (var cAttr in actionAttributes)
{
Console.WriteLine("Attrs: " + cAttr.GetType());
}
}
return itce;
}
Where in the variable itce I just retrieve a previously allocated instance of a type that contains those attributes using a factory pattern.
The thing I want it's is actual value, but I only can read it's class definition full name. It's obvious, I am asking for it to the GetType() method, but I have only four opts available like ToString() and things like that. I am imagine that I am missing some type cast probably? Don't know. Hope someone could help me with this.
BTW, Action type it's just an enum:
enum Action
{
Read,
Write
}
and, a simple example of the usage of the attributes:
public class Device : Display
{
[Action(Action.Read)]
[Action(Action.Write)]
public string device_name { get; set; }
public Device(string device_name)
{
this.device_name = device_name;
}
}
So, the idea, it's to retrieve the value of the attribute whenever a type has a field annotated. Above, Device has two annotations, Read and Write. I want to recover with reflection the actual value or values attached to that field.
device_name has two attributes, so I need to recover Action.Read and Action.Write.
Thanks.
As #freakish pointed, solution it's pretty straightfoward.
object[] actionAttributes = field.GetCustomAttributes(typeof(ActionAttribute), false);
foreach (var cAttr in actionAttributes)
{
var attr = (ActionAttribute)cAttr;
Console.WriteLine("Attrs: " + attr.getThis());
}
Casting the var cAttr to the attribute type allows me to easily access the info that holds the field attribute.
Instead of using var for your loop control variable and it being object, specify the type as your attribute:
object[] actionAttributes = field.GetCustomAttributes(typeof(ActionAttribute), false);
foreach (ActionAttribute cAttr in actionAttributes)
{
Console.WriteLine("Attrs: " + cAttr.getThis());
}
Of course, you really ought to be using a public read-only property, rather than a private field and a method, to access that value.
Related
I am trying to use reflection for getting the property name declared and its value, I am able to get the declared property name using property info the main concern I am having is I want to get the value for the property and I don't know the object type so I cant cast directly.
I know we need to use item.GetValue(object) but here the object, I need to pass using reflection.
For example, if you see the below code
Class structure
public abstract class ObjectInputs{}
public class ValveInputs : ObjectInputs
{
public Conditions Conditions { get; set; } = new Conditions();
}
public class Conditions :IExportable
{
[CanExportAttribute]
public string north {get;set;}
}
Method
public void Append(Scenario scenario)
{
var scenarioInputs = (commonDomain.ObjectInputs)scenario.Inputs; // ObjectInputs is an abstract class
var exportableInputs = scenarioInputs.GetType().GetProperties().Where(x =\> typeof(IExportable).IsAssignableFrom(x.PropertyType)); // I extraced property having interface IExportable
var listOfExportableProperties = new ScenarioExtract();
foreach (var exportableInput in exportableInputs)
{
var allProperties = ((System.Reflection.TypeInfo)exportableInput.PropertyType).DeclaredProperties; // Got all the property details
var propertyHavingAttribute = allProperties.Where(x =\> x.CustomAttributes.Where(z =\> z.AttributeType == typeof(CanExportAttribute)).Any()).ToArray(); // Got the properties which i need to extract.
The issue is here, if i do this then its creating a new instance and the values of each properties are set to default. I want to cast the exportableInput to its type (I cant hard code the type casting) so that i can use the value below.
object destination = Activator.CreateInstance(scenarioInputs.GetType());
foreach (var item in propertyHavingAttribute)
{
var detail = new InputPropertyDetail { InputName = item.Name, InputValue = \*\*item.GetValue(destination).ToString() \*\*}; \*\*want to use value here\*\*
listOfExportableProperties.PropertyDetails.Add(detail);
}
}
spreadsheetBuilder.AppendComponenet(listOfExportableProperties);
}
If you're using Activator.CreateInstance, it will always create a new instance (as the name inplies) with default values. Instead you must use the value of the exportableInput property.
object destination = exportableInput.GetValue(scenarioInputs);
Then you can get the actual value of the exportable property of the instance with InputValue = item.GetValue(destination).ToString().
I've created a data class that I plan to use to send data to be persisted in the database and to return data from the database in a strongly typed way. In addition to its properties, the class contains a Dictionary that I populate in the constructor with the name of and reference to each property. This makes the properties enumerable and enables me to iterate through them using 'foreach'.
This works great when setting property values and sending the object to be persisted in the database. I can iterate through the Dictionary keys, get the value of each property, and add a SqlParameter for each property using the key as the parameter name and the property value as the parameter value.
However, going the other way doesn't work. I can iterate through the Dictionary keys and get the value of each column in each row of the SqlDataReader, but when I try to assign these values to my data object using the Dictionary's reference to the corresponding object property, a curious thing occurs. The assignments succeed, BUT the data object properties all retain their initial, default values. I can view the data object properties and see these initial, default values. I can also view the Dictionary entry values and see the updated values that were read and assigned from the SqlDataReader.
This makes no sense. The Dictionary is supposed to provide access to each property (the 'object' generic type) via its key (the 'string' generic type), but its acting like its maintaining a separate copy of each Dictionary 'KeyValuePair'.
What gives?
I'm doing all this in C# in the context of an ASP.NET Core 2.1.1 project running on macOS 10.13.6 High Sierra.
I've searched StackOverflow extensively, and I see lots of recommendations for using reflection to do this type of thing. I'll refactor my code to use reflection if necessary, but I'd really like to understand where and how my mental model for what's happening is off.
An explanation of what's happening and why would be MOST appreciated.
Example Data Class with Property Dictionary
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
using Newtonsoft.Json;
namespace MyOrg.MyProj.Data
{
[DataContract]
public class DataObj
{
#region Attributes
[Required]
[DataMember(Name = "dataObjectId")]
public Int64 DataObjectId { get; set; }
[Required]
[DataMember(Name = "guid")]
public Guid Guid { get; set; }
public virtual Dictionary<string, object> DataMembers { get; set; } //NOTE: Implements the IEnumerable interface in order to support 'foreach' operations, etc on 'DataObj' class attributes
#endregion Attributes
#region Constructors
public DataObj(Int64 dataObjectId, Guid guid)
{
try
{
DataObjectId = dataObjectId;
Guid = guid;
DataMembers = new Dictionary<string, object>
{
{ "DataObjectId", DataObjectId },
{ "Guid", Guid }
};
}
catch (Exception e)
{
Console.WriteLine($"RUNTIME EXCEPTION while INSTANTIATEing DataObj, " + e.Message + ", " + e.StackTrace);
}
}
#endregion Constructors
#region Methods
/// <summary>
/// Implements the IEnumerable interface in order to support 'foreach' operations, etc on 'DataObj' class attributes
/// </summary>
/// <returns>Enumerator</returns>
public Dictionary<string, object>.Enumerator Enumerator()
{
return DataMembers.GetEnumerator(); //NOTE: Return the Dictionary object's IEnumerator rather than implementing IEnumerable for the 'DataObj' class itself
}
#endregion Methods
Example Data Access Class (excerpt)
reader = command.ExecuteReader();
dataObjList = new List<DataObj>();
if (reader.HasRows)
{
while (reader.Read())
{
tempDataObj = new DataObj(-1, new Guid("00000000-0000-0000-0000-000000000000"));
keys = new List<String>(tempDataObj.DataMembers.Keys); //NOTE: Can't modify a Dictionary while iterating through it. See the 'Why This Error?' section of https://stackoverflow.com/questions/604831/collection-was-modified-enumeration-operation-may-not-execute
foreach (String key in keys)
{
tempDataObj.DataMembers[key] = reader[key];
}
dataObjList.Add(tempDataObj);
For 'key' = 'DataObjectId', 'Guid', etc, I expect the value of tempDataObj.DataObjectId, tempDataObj.Guid, etc to be set to the value returned from the database in 'reader[key]'.
Instead, it retains its initial, default value as set in the constructor, i.e. '-1'. This is true for both value and reference data types.
However, when I inspect tempDataObj.DataMembers["DataObjectId"], it has been set to the value returned from the database in 'reader[key]'.
Inspecting the Object Property and Dictionary Values
tempDataObj.DataMembers["DataObjectId"] should be referencing the tempDataObj.DataObjectId property, etc, but the Dictionary appears to be maintaining its own value rather than providing an object reference to the 'DataObjectId' property.
What's going on here? Thank you!
You're storing the data twice - once in a Dictionary, and a second time in a field. There's no need to store it twice. Just do this:
[DataContract]
public class DataObj
{
[Required]
[DataMember(Name = "dataObjectId")]
public Int64 DataObjectId
{
get => (long)DataMembers[nameof(DataObjectId)];
set => DataMembers[nameof(DataObjectId)] = value;
}
[Required]
[DataMember(Name = "guid")]
public Guid Guid
{
get => (Guid)DataMembers[nameof(Guid)];
set => DataMembers[nameof(Guid)] = value;
}
public Dictionary<string, object> DataMembers { get; } = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
public DataObj(Int64 dataObjectId, Guid guid)
{
DataObjectId = dataObjectId;
Guid = guid;
}
public Dictionary<string, object>.Enumerator Enumerator()
{
return DataMembers.GetEnumerator();
}
}
FYI, you can also look at using an ExpandoObject, which lets you access something in a way that looks like a class, but is really just a Dictionary. https://learn.microsoft.com/en-us/dotnet/api/system.dynamic.expandoobject?view=netframework-4.7.2
I have never used an ExpandoObject and I think the whole idea is as perverse as VBA's default of option explicit being off and On Error Resume Next. On the other hand, I don't deal with databases much.
I see two (main) routes to do what you want. In both cases you should implement a custom indexer.
In the indexer explicitly check the name given to it and get or set the field or property accordingly.
Use reflection, i.e. GetField() or GetProperty(), to get the field or property and GetValue() or SetValue() to get or set the values.
Below is a demonstration where ExposeByExplicitIndexer0 and its descendants use way 1 and ExposeByIndexerUsingReflection0 and its descendants use way 2.
public class ExposeByExplicitIndexer0
{
public int Int0 = 1;
public string String0 = "A";
public virtual object this[string name]
{
get
{
switch (name)
{
case "Int0":
return this.Int0;
case "String0":
return this.String0;
default:
throw new IndexOutOfRangeException();
}
}
set
{
switch (name)
{
case "Int0":
this.Int0 = (int)value;
break;
case "String0":
this.String0 = (string)value;
break;
default:
throw new IndexOutOfRangeException();
}
}
}
}
public class ExposeByExplicitIndexer1 : ExposeByExplicitIndexer0
{
protected Guid _Guid1 = Guid.Empty;
public Guid Guid1
{
get
{
return this._Guid1;
}
set
{
this._Guid1 = value;
}
}
public override object this[string name]
{
get
{
switch (name)
{
case "Guid1":
return this.Guid1;
default:
return base[name];
}
}
set
{
switch (name)
{
case "Guid1":
this.Guid1 = (Guid)value;
break;
default:
base[name] = value;
break;
}
}
}
}
public class ExposeByIndexerUsingReflection0
{
public object this[string name]
{
get
{
FieldInfo fieldInfo;
if ((fieldInfo = this.GetType().GetField(name)) != null)
{
return fieldInfo.GetValue(this);
}
PropertyInfo propertyInfo;
if ((propertyInfo = this.GetType().GetProperty(name)) != null)
{
return propertyInfo.GetValue(this);
}
throw new IndexOutOfRangeException();
}
set
{
FieldInfo fieldInfo;
if ((fieldInfo = this.GetType().GetField(name)) != null)
{
fieldInfo.SetValue(this, value);
return;
}
PropertyInfo propertyInfo;
if ((propertyInfo = this.GetType().GetProperty(name)) != null)
{
propertyInfo.SetValue(this, value);
return;
}
throw new IndexOutOfRangeException();
}
}
}
public class ExposeByIndexerUsingReflection1 : ExposeByIndexerUsingReflection0
{
public int Int1 = 1;
public string String1 = "A";
}
public class ExposeByIndexerUsingReflection2 : ExposeByIndexerUsingReflection1
{
protected Guid _Guid2 = Guid.Empty;
public Guid Guid2
{
get
{
return this._Guid2;
}
set
{
this._Guid2 = value;
}
}
}
public class Program
{
static void Main(string[] args)
{
Guid newGuid = Guid.NewGuid();
Console.WriteLine("Expose by explicit indexer:");
ExposeByExplicitIndexer1 exposeByExplicitIndexer1 = new ExposeByExplicitIndexer1();
exposeByExplicitIndexer1["Int0"] = 10;
exposeByExplicitIndexer1["String0"] = "AAA";
exposeByExplicitIndexer1["Guid1"] = newGuid;
Console.WriteLine("output via indexer:");
Console.WriteLine(exposeByExplicitIndexer1["Int0"]);
Console.WriteLine(exposeByExplicitIndexer1["String0"]);
Console.WriteLine(exposeByExplicitIndexer1["Guid1"]);
Console.WriteLine("output via fields or properties:");
Console.WriteLine(exposeByExplicitIndexer1.Int0);
Console.WriteLine(exposeByExplicitIndexer1.String0);
Console.WriteLine(exposeByExplicitIndexer1.Guid1);
Console.WriteLine("Expose by indexer using reflection:");
ExposeByIndexerUsingReflection2 exposeByIndexerUsingReflection2 = new ExposeByIndexerUsingReflection2();
exposeByIndexerUsingReflection2["Int1"] = 10;
exposeByIndexerUsingReflection2["String1"] = "AAA";
exposeByIndexerUsingReflection2["Guid2"] = newGuid;
Console.WriteLine("output via indexer:");
Console.WriteLine(exposeByIndexerUsingReflection2["Int1"]);
Console.WriteLine(exposeByIndexerUsingReflection2["String1"]);
Console.WriteLine(exposeByIndexerUsingReflection2["Guid2"]);
Console.WriteLine("output via fields or properties:");
Console.WriteLine(exposeByIndexerUsingReflection2.Int1);
Console.WriteLine(exposeByIndexerUsingReflection2.String1);
Console.WriteLine(exposeByIndexerUsingReflection2.Guid2);
Console.Read();
}
}
In way 1 every descendant that adds new fields or properties has to extend the indexer. That's more work in general but also offers an easy way of flexibility i.e. for adding some casts or expose some field or property via an alias, etc.
Way 2 needs less effort in the descendants. But being as flexible as in way 1 may become more difficult in turn. Maybe some mixed solution is also possible overriding the indexer in some descendant to inject special logic.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Finding the Variable Name passed to a Function in C#
The class below contains the field city.
I need to dynamically determine the field's name as it is typed in the class declaration
i.e. I need to get the string "city" from an instance of the object city.
I have tried to do this by examining its Type in DoSomething() but can't find it when examining the contents of the Type in the debugger.
Is it possible?
public class Person
{
public string city = "New York";
public Person()
{
}
public void DoSomething()
{
Type t = city.GetType();
string field_name = t.SomeUnkownFunction();
//would return the string "city" if it existed!
}
}
Some people in their answers below have asked me why I want to do this.
Here's why.
In my real world situation, there is a custom attribute above city.
[MyCustomAttribute("param1", "param2", etc)]
public string city = "New York";
I need this attribute in other code.
To get the attribute, I use reflection.
And in the reflection code I need to type the string "city"
MyCustomAttribute attr;
Type t = typeof(Person);
foreach (FieldInfo field in t.GetFields())
{
if (field.Name == "city")
{
//do stuff when we find the field that has the attribute we need
}
}
Now this isn't type safe.
If I changed the variable "city" to "workCity" in my field declaration in Person this line would fail unless I knew to update the string
if (field.Name == "workCity")
//I have to make this change in another file for this to still work, yuk!
{
}
So I am trying to find some way to pass the string to this code without physically typing it.
Yes, I could declare it as a string constant in Person (or something like that) but that would still be typing it twice.
Phew! That was tough to explain!!
Thanks
Thanks to all who answered this * a lot*. It sent me on a new path to better understand lambda expressions. And it created a new question.
Maybe you need this. Works fine.
I found this here.
static void Main(string[] args)
{
var domain = "matrix";
Check(() => domain);
Console.ReadLine();
}
static void Check<T>(Expression<Func<T>> expr)
{
var body = ((MemberExpression)expr.Body);
Console.WriteLine("Name is: {0}", body.Member.Name);
Console.WriteLine("Value is: {0}", ((FieldInfo)body.Member)
.GetValue(((ConstantExpression)body.Expression).Value));
}
Output will be:
Name is: 'domain'
Value is: 'matrix'
I know this is old question, but I was trying to achieve the same and google sent me here. After many hours I finally found a way. I hope somebody else will find this useful.
There are actually more ways to accomplish this:
static void Main(string[] args)
{
GetName(new { var1 });
GetName2(() => var1);
GetName3(() => var1);
}
static string GetName<T>(T item) where T : class
{
return typeof(T).GetProperties()[0].Name;
}
static string GetName2<T>(Expression<Func<T>> expr)
{
return ((MemberExpression)expr.Body).Member.Name;
}
static string GetName3<T>(Func<T> expr)
{
return expr.Target.GetType().Module.ResolveField(BitConverter.ToInt32(expr.Method.GetMethodBody().GetILAsByteArray(), 2)).Name;
}
The first is fastest. The last 2 are approx 20 times slower than the 1st one.
http://abdullin.com/journal/2008/12/13/how-to-find-out-variable-or-parameter-name-in-c.html
city in this case is an instance of type string. When you call .GetType() you return the actual string type, which has no knowledge at all of your particular city instance.
I'm having a hard time seeing why you can't just type "city" in the code as a string literal here, if that's what you need. Perhaps it would help if you shared what you want to use this value for and in what circumstances you will call your DoSomething() function.
At the moment, my best guess is that what you really want to do is reflect the entire Person class to get a list of the fields in that class:
public void DoSomething()
{
MemberInfo[] members = this.GetType().GetMembers();
// now you can do whatever you want with each of the members,
// including checking their .Name properties.
}
Okay, based on your edit I have some more for you.
You can find the name of fields that are decorated with your attribute at run-time like this:
Type t = typeof(Person);
foreach (MemberInfo member in t.GetMembers()
.Where(m =>
m.GetCustomAttributes(typeof(MyCustomAttribute)).Any() ) )
{
// "member" is a MemberInfo object for a Peson member that is
// decorated with your attribute
}
You can also use binding flags in the first GetMembers() call to limit it to just fields, if you want.
You mentioned "i.e. I need to get the string "city" from an instance of the object city."
Are you looking to get the field name from the value of the field.
For example:If there are 2 Person object one with city "New York" and the other with city "London", are you looking for the function to return "city". Is this what you mean by dynamic?
With your current design you will always need to compare the name of the field from the FieldInfo against a string.
What if you instead decouple this so that you hold the identifier to use for comparison purposes during reflection as part of the attribute.
Something like this:
public enum ReflectionFields
{
CITY = 0,
STATE,
ZIP,
COUNTRY
}
[AttributeUsage(AttributeTargets.Field,AllowMultiple=false)]
public class CustomFieldAttr : Attribute
{
public ReflectionFields Field { get; private set; }
public string MiscInfo { get; private set; }
public CustomFieldAttr(ReflectionFields field, string miscInfo)
{
Field = field;
MiscInfo = miscInfo;
}
}
public class Person
{
[CustomFieldAttr(ReflectionFields.CITY, "This is the primary city")]
public string _city = "New York";
public Person()
{
}
public Person(string city)
{
_city = city;
}
}
public static class AttributeReader<T> where T:class
{
public static void Read(T t)
{
//get all fields which have the "CustomFieldAttribute applied to it"
var fields = t.GetType().GetFields().Where(f => f.GetCustomAttributes(typeof(CustomFieldAttr), true).Length == 1);
foreach (var field in fields)
{
var attr = field.GetCustomAttributes(typeof(CustomFieldAttr), true).First() as CustomFieldAttr;
if (attr.Field == ReflectionFields.CITY)
{
//You have the field and you know its the City,do whatever processing you need.
Console.WriteLine(field.Name);
}
}
}
}
public class Program
{
public static void Main(string[] args)
{
PPerson p1 = new PPerson("NewYork");
PPerson p2 = new PPerson("London");
AttributeReader<PPerson>.Read(p1);
AttributeReader<PPerson>.Read(p2);
}
}
You can now freely rename _city field of Person to something else and your calling code will still work since the code using reflection is trying to identify the field using the ReflectionFields enum value set as part of initialization of the attribute set on the field.
Yes its possible !!!
Try this out...
public string DoSomething(object city)
{
return city.GetType().GetProperty("Name",typeof(string)).GetValue(city,null);
}
Two things here.
Number one, as someone above pointed out, you're getting the Type for string, not for Person. So typeof(Person).GetMembers() will get you the list of members.
Number two, and more importantly, it looks like you're misunderstanding the purpose of attributes. In general attributes are used to mark a member for specific processing or to add additional information. Here you're using the name to indicate what processing you want, and the attribute to specify parameters, which is mixing of metaphors, or something.
Abhijeet's answer is more appropriate, you mark the field as a city field, then do what you like with it. Where I disagree is that I would use different attribute classes, rather than an enumeration.
Something like:
public class MyAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Field)]
public class MyCityAttribute : MyAttribute
{
}
[AttributeUsage(AttributeTargets.Field]
public class MyNameAttribute: MyAttribute
{
}
public class Person
{
[MyCity]
public string city = "New York";
[MyCity]
public string workCity = "Chicago";
[MyName]
public string fullName = "John Doe";
public Person()
{
}
public void DoSomething()
{
Type t = typeof(Person);
FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.Public);
foreach (var field in fields)
{
MyAttribute[] attributes = field.GetCustomAttributes(typeof(MyAttribute));
if (attributes.Count > 0)
{
if (attributes[0] is MyCityAttribute)
{
//Dosomething for city
break;
}
if (attributes[0] is MyNameAttribute)
{
//Dosomething for names
break;
}
}
}
}
}
This would allow you to use different parameters for MyCity vs MyName that would make more sense in the context of processing each.
I think with your 'yuk' comment above, you hit the nail on the head. That you would have to change a string constant if you rename your variable is an indicator that you're doing something wrong.
t.GetField("city", BindingFlags.Public | BindingFlags.Instance);
or you can call GetFields() to get all fields
You need to call get type on the class Person. The iterate the fields of the class as in the answer below
This is not possible (I think it actually is but involes several hacks and using lambdas). If you want to store attributes about a Person and be able to get the name of the attribute easily, I suggest using a Dictionary<TKey, TValue> from the System.Collections.Generic namespace.
And you can always make public properties that wrap the dictionary.
public class Person
{
Dictionary<string, string> attributes = new Dictionary<string, string();
public string City
{
get { return attributes["city"]; }
set { attributes["city"] = value; }
}
public Person()
{
City = "New York";
}
}
And you can get a list of all attributes with attributes.Keys.
Have a look at this post as it looks similar to what you're trying to do:
Finding the variable name passed to a function
(especially Konrad Rudolph's answer) Another approach could be to just add "city" as one of the parameters in the attribute and fish that out later.
You are already looping through the collection of FieldInfo objects. Look for your attribute on those and when you find the FieldInfo that contains your attribute, you have the one you want. Then call .Name on it.
system.reflection.fieldinfo.attributes
i want to do a class constructor that takes a dicionary as parameter and initialize all the class variables that are listed as key in the dictionary, after of course a type conversion:
public class User
{
public int id;
public string username;
public string password;
public string email;
public int mana_fire;
public int mana_water;
public int mana_earth;
public int mana_life;
public int mana_death;
public User ()
{
}
public User(Dictionary<string,string> dataArray){
FieldInfo[] classVariablesInfoList = typeof(User).GetFields();
for(int i = 0; i < classVariablesInfoList.Length; i++)
{
if(dataArray.ContainsKey(classVariablesInfoList[i].Name)){
//missing code here :)
//need something like classVariable= dataArray["classVariablesInfolist[i].name"]; ?
}
}
}
}
but i can't find out how to do this!
Can you please help? :)
You can use the SetValue frunction from reflection:
FieldInfo f = classVariablesInfoList[i];
if (f.ReflectedType == typeof(int))
{
f.SetValue(this, Convert.ToInt32(dataArray[f.Name]));
}
else
{
f.SetValue(this, dataArray[classVariablesInfoList[i].Name]);
}
But it is a really uncommon way to do this with a dictionary. You should considder accessing the fields directly or add parameters to the constructor for any field. And fields should never be public - use properties instead.
The following will work if Convert.ChangeType() is able to handle the conversion. There are a lot of problems waiting to occur, for example handling numbers or dates where the string representation depends on the locale. I would really suggest to use usual typed constructor parameters or standard (de)serialization mechanism if possible. Or at least use a dictionary containing objects instead of strings to get rid of the conversion, again if possible.
public User(Dictionary<String, String> data)
{
var fields = typeof(User).GetFields();
foreach (field in fields)
{
if (data.ContainsKey(field.Name))
{
var value = Convert.ChangeType(data[field.Name], field.MemberType);
field.SetValue(this, value);
}
}
}
I would like to separate your problem into two parts.
1. Applying conversion
The FieldInfo type present a FieldType property that is the actual type of the field, using this Type we can use the non-generic ChangeType method of System.Convert, this method will be able convert some types to others. Luckily it support String to Int.
Usage:
Convert.ChangeType(OLD_VALUE, TARGET_TYPE);
2. Setting the field
The field info class has a SetValue method (FieldInfo.SetValue), it has two parameters, the first one is the current (ie. this) instance (or the instance you wish to change). the second is the new value you wish to set.
Putting it all together
[...]
var fieldInfo = classVariablesInfoList[i];
var name = fieldInfo.Name;
var targetType = fieldInfo.Type;
var value = Convert.ChangeType(dataArray[name], targetType);
classVariablesInfoList[i].SetValue(this, value);
Consider this example:
public interface IAnimal
{
}
public class Cat: IAnimal
{
}
public class DoStuff
{
private Object catList = new List<Cat>();
public void Go()
{
// I want to do this, but using reflection instead:
if (catList is IEnumerable<IAnimal>)
MessageBox.Show("animal list found");
// now to try and do the above using reflection...
PropertyInfo[] properties = this.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
//... what do I do here?
// if (*something*)
MessageBox.Show("animal list found");
}
}
}
Can you complete the if statement, replacing something with the correct code?
EDIT:
I noticed that I should have used a property instead of a field for this to work, so it should be:
public Object catList
{
get
{
return new List<Cat>();
}
}
You can look at the properties' PropertyType, then use IsAssignableFrom, which I assume is what you want:
PropertyInfo[] properties = this.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
if (typeof(IEnumerable<IAnimal>).IsAssignableFrom(property.PropertyType))
{
// Found a property that is an IEnumerable<IAnimal>
}
}
Of course, you need to add a property to your class if you want the above code to work ;-)
Please note that in your example, catList would not be found with GetType().GetProperties (). You would use GetType().GetFields () instead.
If you are trying to determine whether the property is defined as IEnumerable you can do this:
if (typeof(IEnumerable<IAnimal>) == property.PropertyType)
{
MessageBox.Show("animal list found");
}
If you want to know if you can assign the value of the property into a IEnumerable<IAnimal>, do this:
if (typeof(IEnumerable<IAnimal>).IsAssignableFrom (property.PropertyType))
{
MessageBox.Show("animal list found");
}
If the property type is not specific enough (like object Animal{get;set;}) to get your answer, you will need to get the value to decide. You can do this:
object value = property.GetValue(this, null);
if (value is IEnumerable<IAnimal>)
{
MessageBox.Show("animal list found");
}
Another way to do it is just call GetProperties() on the interface from within the object, opposed to on the object itself.
public static void DisplayObjectInterface(object source, Type InterfaceName)
{
// Get the interface we are interested in
var Interface = source.GetType().GetInterface(InterfaceName.Name);
if (Interface != null)
{
// Get the properties from the interface, instead of our source.
var propertyList = Interface.GetProperties();
foreach (var property in propertyList)
Debug.Log(InterfaceName.Name + " : " + property.Name + "Value " + property.GetValue(source, null));
}
else
Debug.Log("Warning: Interface does not belong to object.");
}
I like to make InterfaceName parameter a Type to avoid any typo errors when looking up GetInterface() by string name.
Usage:
DisplayObjectInterface(Obj, typeof(InterFaceNameGoesHere));
EDIT: I just noticed that your example was a collection, this will not work on a collection passed as a whole. You would have to pass each item individually. I'm tempted to delete but this will probably help others that google this same question looking for a non-collection solution.