I have an object with some properties and a dictionary which holds a temporary value for each of property. The key of this dictionary is a string with the same name of the property, while the value is an object.
What I want to do is to build a save method that reads the dictionary's keys and set the corresponding property to the value found in the dictionary.
So I thought about reflection but it's not as easy as I thought.
Here's a sample class:
public class Class{
public string Property1 { get; set; }
public int Property2 { get; set; }
public Dictionary<string, object> Settings { get; set; }
public void Save()
{
foreach (string key in Settings.Keys)
{
// PSEUDOCODE
get the property called like the key
get its type
get the value of hte key in the dictionary
cast this object to the property's value
set the property to the casted object
}
}
}
The reason why I'm not posting my code is beacuse I don't understand how to do casting and similar stuff, so I wrote a little bit of pseudocode to let you understand what I'm trying to achieve.
Is there anyone that can point me to the right direction?
Here:
//Get the type
var type= this.GetType();
foreach (string key in Settings.Keys)
{
//Get the property
var property = type.GetProperty(key);
//Convert the value to the property type
var convertedValue = Convert.ChangeType(Settings[key], property.PropertyType);
property.SetValue(this, convertedValue);
}
Not tested, but it should work.
Related
I want to get the value of a field of an object by using a string as variable name.
I tried to do this with reflection:
myobject.GetType().GetProperty("Propertyname").GetValue(myobject, null);
This works perfectly but now I want to get the value of "sub-properties":
public class TestClass1
{
public string Name { get; set; }
public TestClass2 SubProperty = new TestClass2();
}
public class TestClass2
{
public string Address { get; set; }
}
Here I want to get the value Address from a object of TestClass1.
You already did everything you need to do, you just have to do it twice:
TestClass1 myobject = ...;
// get SubProperty from TestClass1
TestClass2 subproperty = (TestClass2) myobject.GetType()
.GetProperty("SubProperty")
.GetValue(myobject, null);
// get Address from TestClass2
string address = (string) subproperty.GetType()
.GetProperty("Address")
.GetValue(subproperty, null);
Your SubProperty member is actually a Field and not a Property, that is why you can not access it by using the GetProperty(string) method. In your current scenario, you should use the following class to first get the SubProperty field, and then the Address property.
This class will allow you to specify the return type of your property by closing the type T with the appropriate type. Then you will simply need to add to the first parameter the object whose members you are extracting. The second parameter is the name of the field you are extracting while the third parameter is the name of the property whose value you are trying to get.
class SubMember<T>
{
public T Value { get; set; }
public SubMember(object source, string field, string property)
{
var fieldValue = source.GetType()
.GetField(field)
.GetValue(source);
Value = (T)fieldValue.GetType()
.GetProperty(property)
.GetValue(fieldValue, null);
}
}
In order to get the desired value in your context, simply execute the following lines of code.
class Program
{
static void Main()
{
var t1 = new TestClass1();
Console.WriteLine(new SubMember<string>(t1, "SubProperty", "Address").Value);
}
}
This will give you the value contained in the Address property. Just make sure you first add a value to the said property.
But should you actually want to change the field of your class into a property, then you should make the following change to the original SubMember class.
class SubMemberModified<T>
{
public T Value { get; set; }
public SubMemberModified(object source, string property1, string property2)
{
var propertyValue = source.GetType()
.GetProperty(property1)
.GetValue(source, null);
Value = (T)propertyValue.GetType()
.GetProperty(property2)
.GetValue(propertyValue, null);
}
}
This class will now allow you to extract the property from your initial class, and get the value from the second property, which is extracted from the first property.
try
myobject.GetType().GetProperty("SubProperty").GetValue(myobject, null)
.GetType().GetProperty("Address")
.GetValue(myobject.GetType().GetProperty("SubProperty").GetValue(myobject, null), null);
I am trying to find a way to take a class's property and pass it to a method along with another variable to update the property based on conditions. For example
The class
public class MyClass{
public string? Prop1 { get; set; }
public string? Prop2 { get; set; }
public bool? Prop3 { get; set; }
public DateTime? Prop4 { get; set; }
... etc...
}
Test code I would like to get to work...:
var obj = new MyClass();
MyCheckMethod(ref obj.Prop1, someCollection[0,1]);
in the method:
private void MyCheckMethod(ref Object obj, string value)
{
if (!string.isnullorempty(value))
{
// data conversion may be needed here depending on data type of the property
obj = value;
}
}
I want to be able to pass any property of any class and update the property only after validating the value passed in the method. I was hoping I could do this with generics, but I haven't yet found a way to do so. Or if I am over complicating what I need to do.
The problem is that there may be a bit more to the validation of the passed in value than just a simple isnullorempy check.
I also thought about doing something like this:
private void MyCheckMethod(ref object obj, Action action)
Then I could do something like this:
...
MyCheckMethod(ref obj.Prop1, (somecollection[0,1]) => {
... etc....
})
So I am looking for some guidance on how to proceed.
updated info:
The incoming data is all in string format (this is how a 3rd party vendor supplies the data). The data is supplied via API call for the 3rd party product... part of their SDK. However in my class I need to have proper data types. Convert string values to datetime for dates, string values to int for int data types, etc... . The other caveat is that if there isnt a valid value for the data type then the default value of the property should be NULL.
Additional Information:
The incoming data is always in string format.
eg:
I have to update a boolean property.
The incoming value is "". I test to see if the string Value isNullOrEmpty. It is so I dont do anything to property.
The next property datatype is decimal.
The incoming value is "0.343".
I Test to see if the string value is NullorEmpty. It isnt so I can update the property once I do a convert etc.....
Hope this helps.
Thanks
Full solution after edits:
public static class Extensions
{
//create other overloads
public static void MyCheckMethodDate<TObj>(this TObj obj,Expression<Func<TObj,DateTime>> property, string value)
{
obj.MyCheckMethod(property, value, DateTime.Parse);
}
public static void MyCheckMethod<TObj,TProp>(this TObj obj,Expression<Func<TObj,TProp>> property, string value,Func<string, TProp> converter)
{
if(string.IsNullOrEmpty(value))
return;
var propertyInfo = ((MemberExpression)property.Body).Member as PropertyInfo;
if(null != propertyInfo && propertyInfo.CanWrite)
{
propertyInfo.SetValue(obj, converter(value));
}
}
}
public class Obj
{
public object Prop1{get;set;}
public string Prop2{get;set;}
public DateTime Prop3{get;set;}
}
public class Program
{
public static void Main()
{
var obj = new Obj();
obj.MyCheckMethodDate(x=>x.Prop3, "2018-1-1");
Console.WriteLine(obj.Prop3);
}
}
You can pass a lambda expression:
void DoSomething<T>(Expression<Func<T>> property)
{
var propertyInfo = ((MemberExpression)property.Body).Member as PropertyInfo;
if (propertyInfo == null)
{
throw new ArgumentException("The lambda expression 'property' should point to a valid Property");
}else{
var name = ((MemberExpression)property.Body).Member.Name;
var value = property.Compile();
//Do whatever you need to do
}
}
To use:
DoSomething(() => obj.Property1);
You can't pass a reference to an arbitrary property. A property is basically implemented as two methods, set_Property and get_Property, and there's no way to bundle these together.
One option is to have your checker function take delegates to access the property. For example:
private void MyCheckMethod(Func<string> getter, Action<string> setter)
{
var value = getter();
var newValue = value.ToUpper();
setter(value);
}
So now you would say something like this:
public class MyClass
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
}
var c = new MyClass();
MyCheckMethod(() => c.Prop1, value => c.Prop1 = value);
Use reflection with compiled expressions.
This performs better than reflection and a little bit slower than native code.
It's not type safe, but you can add runtime validation.
I started learning about MVC 6 and I found this tutorial.
The following code is quoted from the linked site:
//TodoItem.cs
namespace TodoApi.Models
{
public class TodoItem
{
public string Key { get; set; }
public string Name { get; set; }
public bool IsComplete { get; set; }
}
}
The TodoItem class will be a value field in a ConcurrentDictionary:
static ConcurrentDictionary<string, TodoItem> _todos =
new ConcurrentDictionary<string, TodoItem>();
The key field which has string type will contain the same value as TodoItem.Key:
public void Add(TodoItem item)
{
item.Key = Guid.NewGuid().ToString();
_todos[item.Key] = item;
}
Does this means that each time a new item is added the the Key will stored twice(once is the key field of the dictionary and once inside the value field) or I am missing something?
I came across this situation in C++ using std::map too and I always used something like this to avoid storing the value of Key two times:
struct Item
{
//std::string Key;
std::string Name;
bool IsComplete;
};
std::map<std::string, Item> items;
// ^^ Item.Key
Does this means that each time a new item is added the the Key will
stored twice(once is the key field of the dictionary and once inside
the value field) or I am missing something?
In .NET System.String type is a reference type, so you shouldn't be worried about the key being stored twice. It will be a single instance in memory to which both the Key of the dictionary and the Key property of the item are simply pointing to. So don't worry about redundancy in this situation. The ConcurrentDictionary structure that you are using here is just a simple wrapper of pointers around your actual data.
Also worth mentioning another interesting property of the System.String type in .NET. Even if you have 2 different instances of a string with the same value the runtime could decide to intern them and they will point to the same data in memory:
string a = "abc";
string b = "abc";
bool res = object.ReferenceEquals(a, b); // true
I'm a PHP Developer...
I need to do a class that can be created and fill of dynamic way, similar to this in PHP.
class Person{
private $name;
private $age;
function __construct($params = array()){
foreach ($this as $key => $val) {
$this -> $key = (isset($params[$key])) ? $params[$key] : "";
}
}
function getName(){
return $this->name;
}
function getAge(){
return $this->age;
}
function setName($value){
$this->name = $value;
}
function setAge($value){
$this->age = $value;
}
}
I read about the reflection in C#, but I don't find the correct way to do.
This is my C# code
public class Person
{
private String _name { get { return _name; } set { _name = value; } }
private int _age { get { return _age; } set { _age = value; } }
public Person()
{
}
public Person(Hashtable _data)
{
PropertyInfo[] propertyInfos;
propertyInfos = typeof(Person).GetProperties(BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var propInfo in propertyInfos)
{
typeof(Person).GetProperty(propInfo.Name).SetValue(this, _data[propInfo.Name]);
}
}
}
In runtime I get an Exception
Object reference not set to an instance of an object.
The typeof(Person) I try to change it to this.getType() and I get the same.
I hope that can help me.
You are grabbing all properties on the object and then looking them up in the hashtable. You likely want the reverse--all objects in the hashtable set to properties on the object. Otherwise you'll get an exception when you don't specify every single member.
As Alexei points out, the NullReferenceException is due to the second call to GetProperties only returning public properties when no BindingFlags are supplied. Since there are no public properties, you get an exception.
Because C# is strongly typed, you run into a number of issues you don't have in PHP. These include setting a value with an object of a type that doesn't match or convert to the property type, entries in your data parameter that don't exist as properties, etc. I've done my best to document the gotchas I see below.
Here is what the Person class would look like (I've cleaned up some of the style and used classes to make it feel more like a C# class):
public class Person
{
private string name { get; set; }
private int age { get; set; }
public Person()
{
}
public Person(IDictionary<string,object> data)
{
foreach (var value in data)
{
// The following line will be case sensitive. Do you need to standardize the case of the input dictionary before getting the property?
PropertyInfo property = typeof(Person).GetProperty(value.Key, BindingFlags.Instance | BindingFlags.NonPublic);
if (property != null)
{
property.SetValue(this, value.Value); // You are allowing any old object to be set here, so be prepared for conversion and casting exceptions
}
else
{
// How do you want to handle entries that don't map to properties? Ignore?
}
}
}
}
And here is an example of usage:
static void Main(string[] args)
{
var person = new Person(new Dictionary<string,object>() {{"name" ,"Mike"}, {"age", 32}});
}
You should stay away from using var if you're new to the language, it only complicates things.
The propInfo in your foreach-loop already is a PropertyInfo, so you don't need to find it again:
BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance;
PropertyInfo[] propertyInfos = typeof(Person).GetProperties(flags);
foreach (PropertyInfo propInfo in propertyInfos)
{
propInfo.SetValue(this, _data[propInfo.Name]);
}
The NullReferenceException is probably caused by the following part of your original code:
typeof(Person).GetProperty(propInfo.Name)...
Since no BindingFlags are provided to the GetProperty() this time, it looks for public instance properties, and when no such property is found, it returns null (that, or _data is null to begin with).
As others have pointed out, your properties currently will cause StackOverflowExceptions. Try changing them to:
private String _name { get; set; }
private int _age { get; set; }
I am wondering why you would want to do this. There may be better, more idiomatic C#, designs to achieve the behavior you want. But we can't know that because there is no additional contextual information mentioned in the question.
So I will simply try to answer your question. The version below takes your code, using auto properties, and a simple dictionary lookup for the initialization of its members from the supplied dictionary. Also note that this does not require any reflection, because there is nothing dynamic about the members of this class.
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person(IDictionary<string, object> data)
{
// What to do if the map does not contain "Name" or "Age" ?
// Right now: initialize to default value.
Name = TryLookup<string>(data, "Name", null);
Age = TryLookup<int>(data, "Age", default(int));
// What to do if the map contains other items that do not
// map to a member variable?
}
private static T TryLookup<T>(IDictionary<string, object> data, string key, T defaultValue)
{
return data.ContainsKey(key) ? (T)data[key] : defaultValue;
}
}
In case you actually really really badly need a dynamic type as opposed to a statically defined type with fixed member properties, you could use an ExpandoObject or alternatively (but this is far from trivial) build a dynamic type using an AssemblyBuilder with a TypeBuilder
I want to implement a custom collection that contains instances of my class.
This is my class, a bit simplified here.
public class Property : IComparable<Property>
{
public string Name;
public string Value;
public string Group;
public string Id;
...
...
public int CompareTo(Property other)
{
return Name.CompareTo(other.Name);
}
}
I am adding instances of Property to a List collection
Public List<Property> properties;
I can iterate through properties or access a specific property through the index position.
I want to however be able to access the property by its Name such that
var myColor = properties["Color"].Value;
and I do not have an efficient way to do this. I assume that properties should be written as a custom list collection class to achieve this. Does anyone have a code sample I can look at?
Thanks for the help.
Easiest methods were already mentioned, but I see two:
Method 1
Convert to dictionary and lookup there.
var props = properties.ToDictionary( x => x.Name );
Property prop = props["some name"];
Method 2
Create your own collection type which would support indexing
by your arbitrary type.
public class PropertyCollection : List<Property>
{
public Property this[string name]
{
get
{
foreach (Property prop in this)
{
if (prop.Name == name)
return prop;
}
return null;
}
}
}
and use this collection instead
PropertyCollection col = new PropertyCollection();
col.Add(new Property(...));
Property prop = col["some name"];
You can use a Dictionary:
Dictionary<string, Property> properties = new Dictionary<string, Property>();
//you add it like that:
properties[prop.Name] = prop;
//then access it like that:
var myColor = properties["Color"];
Use a Dictionary<string,Property> for this purpose. The key will be the property name and the value will be the Property instance itself.