Given the following:
List<T> someList;
Where T is a type of some class:
public class Class1
{
public int test1;
}
public class Class2
{
public int test2;
}
How would you use Reflection to extract the values of test1/test2 stored in each List item? (The field names are provided)
My Attempt:
print(someList[someIndex]
.GetType()
.GetField("test1")
.GetValue(someList) // this is the part I'm puzzled about. What kind of variable should i pass here?
The Error I'm getting:
"Object reference not set to an instance of an object", and according to microsoft docs the variable I should pass to GetValue is "The object whose field value will be returned." - which is what I'm doing.
Thanks for reading!
Add {get;set;} to your properties
public int test1 { get; set; }
var t = someList[0].GetType().GetProperty("test1").GetValue(someList[0], null);
I recommend you can use this method
public class Class1
{
public int test1 { get; set; }
public object this[string propertyName]
{
get { return this.GetType().GetProperty(propertyName).GetValue(this, null); }
set { this.GetType().GetProperty(propertyName).SetValue(this, value, null); }
}
}
var value = someList[0]["test1"];
Related
I want to write a method that should return the value from an object for the specified property.
public class MyClass
{
public int a { get; set; }
public int b { get; set; }
public int c { get; set; }
public int d { get; set; }
}
public int GetValue(string field)
{
MyClass obj=new MyClass();
obj=FromDb(); //get value from db
dynamic temp=obj;
return temp?.field;
}
The above code is only to demonstrate to what I am looking for.
Here I want to pass the property name (i.e a/b/c/d as per my above code) as an input to the method GetValue and it should return the value of that property.
This code is compiling successfully, but in run time it will search for the property name field not the value of field variable.
Any suggestions or workaround will be appreciated.
You can use reflection to get the value:
public int GetValue(string field)
{
MyClass obj = new MyClass();
obj = FromDb(); //get value from db
var property = obj.GetType().GetProperty(field);
return (int)property.GetValue(obj);
}
I have base class that save all property value in a Dictionary. but I want to property value and value in Dictionary has save value. if property value change then change dictionary value and if change value in dictionary , change property value. In Constructor I use reflection but after finish constructor I want not used reflection because of performance.
Type of dictionary value is following (in my project its more complex):
public class AgentProperty
{
public object Value;
public string Name;
public AgentProperty(string name, object value)
{
Value = value;
Name = name;
}
}
The Base Class is following:
public class BaseClass
{
private Dictionary<string,AgentProperty> Dictionary = new Dictionary<string, AgentProperty>();
public AgentProperty this[string key]
{
get { return (AgentProperty)Dictionary[key]; }
set { Dictionary[key] = value; }
}
public void Add(AgentProperty ap)
{
Dictionary.Add(ap.Name, ap);
}
public void SetDict(BaseClass o)
{
var objectType = o.GetType();
foreach (var property in objectType.GetProperties())
{
AgentProperty agentProperty = new AgentProperty(property.Name, property.GetValue(o));
Add(agentProperty);
}
}
}
The sample class that inherited from BaseClass. This class can have any type of property.
public class TmpClass : BaseClass
{
public TmpClass(){
SetDict(this);
}
public string X { get; set; }
public int y { get; set; }
public string Z { get; set; }
public TimeSpan T { get; set; }
}
Is there any way to do this?
I have a little problem, below my code:
public class A
{
public string ObjectA { get; set; }
}
public void Run()
{
A a = new A();
a.ObjectA = "Test number 1";
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
PropertyInfo myPropertyInfo = a.GetType().GetProperty("ObjectA", flags);
object myNewObject = myPropertyInfo.GetValue(a);// Here should be reference
a.ObjectA = "Test number 2";//After this line value myNewObject continued "Test number 1"
}
So my value myNewObject must be in output "Test number 2". Is there any way? It is this at all possible?
Wrong!
You're getting the string rather than the instance of A using reflection.
Changing A.ObjectA doesn't change the string reference. Actually, you're setting a different string to the backing string class field by the ObjectA property...
Auto-properties are syntactic sugar to avoid explicitly declaring class fields to properties which perform nothing when getting or setting their values:
// For example:
public string Text { get; set; }
// is...
private string _text;
public string Text { get { return _text; } set { _text = value; } }
Now turn your code into regular one (no reflection):
A a = new A();
a.ObjectA = "hello world";
object myNewObject = a.ObjectA;
// Do you think now that myNewObject should change...? :)
a.ObjectA = "goodbye";
Is there any way? It is this at all possible?
No.
Maybe you can simulate this behavior using a Func<T>...
Func<object> myNewObjectGetter = () => myPropertyInfo.GetValue(a);
Now, whenever you call myNewObjectGetter() you're going to get the most fresh value of the whole property. BTW, this still doesn't address the impossible!
Is there any way?
No. You can't put a reference to a property into an object variable. Such a variable can only hold a normal value, such as the string you put into it.
That answers the question as asked. You can clarify what you want to achieve and maybe we can suggest a better way.
Probably there is no solution but I show some code
public class MyRows
{
public string Key { get; set; }
public int Id { get; set; }
public object Val { get; set; }
}
public abstract class BasicDTO
{
public int? Id { get; private set; }
public PropertyInfo[] PropertyDTO;
protected Type myType;
public BasicDTO()
{
Load();
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
PropertyDTO = myType.GetProperties(flags);
}
}
public class CustomerDTO : BasicDTO
{
public string Surname { get; set; }
public string Phone { get; set; }
public CustomerDTO() { }
protected override void Load()
{
myType = typeof(CustomerDTO);
}
}
Now my basic method
public void Run(BasicDTO dto)
{
PropertyInfo pi = dto.PropertyDTO.Where(x => x.Name == "Surname").SingleOrDefault();
MyRows mr = new MyRows();
mr.Val = pi.GetValue(dto);//Here I need reference
}
When I change CustomerDTO.Surname my value mr.Val it must also be changed.
As I wrote above, it is probably impossible, but maybe anybody have a idea.
BTW: Value mr.Val I use only for binding (WPF). So maybe you have any other suggestions, how solve problem. I will be grateful for your help
I have been using a custom type for Money with my POCOs and tries to insert this to my database but it is implicitly thrown away by Entity Framework.
This is my code made simple;
My type;
public struct Money
{
private static decimal _value;
public Money(Decimal value)
{
_value = value;
}
public static implicit operator Money(Decimal value)
{
return new Money(value);
}
public static implicit operator decimal(Money value)
{
return _value;
}
}
My object;
public class MyObject
{
[Key]
public int Id { get; set; }
public Money MyMoney { get; set; }
}
My context;
public class Data : DbContext
{
public Data()
: base("Data Source=.;Database=MyTest;Integrated Security=True")
{}
public DbSet<MyObject> MyObject { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<MyObject>()
.Property(p => p.MyMoney).HasColumnName("MyMoney");
}
}
When I use this code I get the following error.
The property 'MyMoney' is not a
declared property on type 'MyObject'.
Verify that the property has not been
explicitly excluded from the model by
using the Ignore method or
NotMappedAttribute data annotation.
Make sure that it is a valid primitive
property.
I guess the problem is the last sentence.... Then, what is a valid primitive property? Is there any other way to take care of this?
You could take of it by explicitly mapping a decimal property and only exposing the Money Property to callers:
public class MyObject
{
[Key]
public int Id { get; set; }
protected decimal MyMoney { get; set; }
public Money MyMoneyStruct
{
get { return (Money)this.MyMoney; }
set { this.MyMoney = (decimal)value; }
}
}
Ok, this went to be my solution.
public class MyObject
{
[Key]
public int Id { get; set; }
public Money MyMoney { get { return (Money)MyMoneyInternal; } set { MyMoneyInternal = (decimal)value; } }
private decimal MyMoneyInternal { get; set; }
}
To be able to read that private property I created a property extension as below. Since some of my properties of type Money is Nullable I had to take care of that as well.
public static class PropertyExtensions
{
public static PrimitivePropertyConfiguration Property<TClass, TProperty>(this EntityTypeConfiguration<TClass> etc, string propertyName)
where TClass : class
where TProperty : struct
{
PrimitivePropertyConfiguration returnValue;
Type type = typeof(TClass);
var propertyInfo = type.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
ParameterExpression parameterExpression = Expression.Parameter(type, "xyz");
MemberExpression memberExpression = Expression.Property((Expression)parameterExpression, propertyInfo);
if (IsNullable(memberExpression.Type))
{
returnValue = etc.Property((Expression<Func<TClass, TProperty?>>)Expression.Lambda(memberExpression, parameterExpression));
}
else
{
returnValue = etc.Property((Expression<Func<TClass, TProperty>>)Expression.Lambda(memberExpression, parameterExpression));
}
return returnValue;
}
private static bool IsNullable(Type type)
{
bool result;
if (type.IsGenericType)
{
var genericType = type.GetGenericTypeDefinition();
result = genericType.Equals(typeof(Nullable<>));
}
else
{
result = false;
}
return result;
}
}
And then I could use that to read my private property.
modelBuilder.Entity<MyObject>()
.Property<MyObject, decimal>("MyMoneyInternal").HasColumnName("MyMoney");
Thank you Steve for taking me in the right direction.
You could also have transformed your "Money" in a Complex class; which would make the exposed properties in-line in your database. With only the Decimal property, it would look like Money_Value (given you expose Value as a property). Let's say you add "Currency" as a string to the class. You would then have also Money_Currency in the database.
To declare to EF that your class is a Complex type, just annotate it with [ComplexType()].
To get the best of Struct + ComplexType, you could even use both at the time : the complex type using the struct internally (the ComplexType just being a wrapper).
Let's suppose I have this object:
[Serializable]
public class MyClass
{
public int Age { get; set; }
public int MyClassB { get; set; }
}
[Serializable]
public class MyClassB
{
public int RandomNumber { get; set; }
}
The XmlSerializer will serialize the object like that:
<MyClass>
<Age>0</age>
<MyClassB>
<RandomNumber>4234</RandomNumber>
</MyClassB>
</MyClass>
How can I made the property Age nullable? IE: to not serialize the property Age when it's under 0?
I tried with the Nullable, but it serialize my object like that:
<MyClass>
<Age d5p1:nil="true" />
<MyClassB>
<RandomNumber>4234</RandomNumber>
</MyClassB>
</MyClass>
By reading the MSDN documentation I found this:
You cannot apply the IsNullable property to a member typed as a value type because a value type cannot contain nullNothingnullptra null reference (Nothing in Visual Basic). Additionally, you cannot set this property to false for nullable value types. When such types are nullNothingnullptra null reference (Nothing in Visual Basic), they will be serialized by setting xsi:nil to true.
source: http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlelementattribute.isnullable.aspx
I understand a value type can't be set to null. A valuetype is always set to something. The serialization can't make the decision to serialize it or not based on it's current value.
I tried with the attributes, but it didn't work out. I tried creating an agecontainer object and manipulate it's serialization with attributes, but it didn't work out.
What I really want is:
<MyClass>
<MyClassB>
<RandomNumber>4234</RandomNumber>
</MyClassB>
</MyClass>
When the property Age is below 0 (zero).
Looks like you'll have to implement custom serialization.
Yeah, that's what I though too, but I'd like to get away without it.
In the application, the object is much more complex, and I would like to not handle the serialization myself.
I just discovered this. XmlSerialier looks for a XXXSpecified boolean property to determine if it should be included. This should solve the problem nicely.
[Serializable]
public class MyClass
{
public int Age { get; set; }
[XmlIgnore]
public bool AgeSpecified { get { return Age >= 0; } }
public int MyClassB { get; set; }
}
[Serializable]
public class MyClassB
{
public int RandomNumber { get; set; }
}
Proof:
static string Serialize<T>(T obj)
{
var serializer = new XmlSerializer(typeof(T));
var builder = new StringBuilder();
using (var writer = new StringWriter(builder))
{
serializer.Serialize(writer, obj);
return builder.ToString();
}
}
static void Main(string[] args)
{
var withoutAge = new MyClass() { Age = -1 };
var withAge = new MyClass() { Age = 20 };
Serialize(withoutAge); // = <MyClass><MyClassB>0</MyClassB></MyClass>
Serialize(withAge); // = <MyClass><Age>20</Age><MyClassB>0</MyClassB></MyClass>
}
Edit: Yes, it is a documented feature. See the MSDN entry for XmlSerializer
Another option is to use a special pattern to create a Boolean field recognized by the XmlSerializer, and to apply the XmlIgnoreAttribute to the field. The pattern is created in the form of propertyNameSpecified. For example, if there is a field named "MyFirstName" you would also create a field named "MyFirstNameSpecified" that instructs the XmlSerializer whether to generate the XML element named "MyFirstName".
Extending Samuel's answer and Greg Beech's comment to the case of a boolean property: if the property is of type bool then you can't write a simple test in the propertySpecified property.
A solution is to use a Nullable<bool> type, then the test in the propertySpecified property is simply property.HasValue. e.g.
using System.Xml.Serialization;
public class Person
{
public bool? Employed { get; set; }
[XmlIgnore]
public bool EmployedSpecified { get { return Employed.HasValue; } }
}
An alternative to using a nullable type for a numeric property (suggested by Greg Beech) is to set the value property to an invalid default value, such as -1, as follows:
using System.ComponentModel;
using System.Xml.Serialization;
public class Person
{
[DefaultValue(-1)]
public int Age { get; set; }
[XmlIgnore]
public bool AgeSpecified { get { return Age >= 0; } }
}
You can use XmlElementAttribute.IsNullable:
[Serializable]
public class MyClass
{
[XmlElement(IsNullable = true)]
public int? Age { get; set; }
public int MyClassB { get; set; }
}
This should help
Make Age int? and..
public bool ShouldSerializeAge() { return Age.HasValue; }
..it does mean adding the ShouldSerializeXXX methods to your class!
You need to do custom XML serialization; see IXmlSerializer.
public class MyClass : IXmlSerializable
{
public int Age { get; set; }
public MyClassB MyClassB { get; set; }
public System.Xml.Schema.XmlSchema GetSchema()
{
// http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.aspx
return null;
}
public void ReadXml(XmlReader reader)
{
if (reader.IsStartElement("Age"))
Age = reader.ReadContentAsInt();
var serializer = new XmlSerializer(typeof(MyClassB));
MyClassB = (MyClassB)serializer.Deserialize(reader);
}
public void WriteXml(XmlWriter writer)
{
if (Age > 0)
{
writer.WriteStartElement("Age");
writer.WriteValue(Age);
writer.WriteEndElement();
}
var serializer = new XmlSerializer(typeof(MyClassB));
serializer.Serialize(writer, MyClassB);
}
}
Forget about Nullable ... ShouldSerializeXXX is a pretty solution.
Here Age will be serialized upon your condition.
[Serializable]
public class MyClass
{
public int Age { get; set; }
public int MyClassB { get; set; }
#region Conditional Serialization
public bool ShouldSerializeAge() { return age > 0; }
#endregion
}
[Serializable]
public class MyClassB
{
public int RandomNumber { get; set; }
}
xsd.exe will autogenerate the XXXSpecified property and accessors if you set the 'minoccurs' attribute as 'minoccurs="0"' for an element ... if you are using a schema to define your xml/class