Hi; I have 4 tables, one of them is main table also there is one to many relation between tables. TID is Foreign key and ID is PK. As a result. i don't want to fill table with classic method. I should access table property and generic <T> I want to set all TID to T_Table ,C_Table, Q_Table
MY CODES(this is test project not real project but logis is the same as real project) Below codes return to me ERROR( in first foreach loop): Null reference exception; Object reference not set to an instance of an object.
using System.Reflection;
namespace App.ReflectionToGeneric
{
class Program
{
static void Main(string[] args)
{
string[] PropertyNames = new string[] { "TID", "AnyID" };
int[] Vals = new int[] { 1, 2 };
new DataManager().Save<QTable>(PropertyNames, Vals);
}
}
public class DataManager
{
IEnumerable<Table> list = new GetData().GetVals();
public void Save<TModel>( string[] PropertyNames, int[] Vals ) where TModel : class, new()
{
var instance = new TModel();
Type calcType = instance.GetType();
// object calcInstance = Activator.CreateInstance(calcType);
foreach (string PropertyName in PropertyNames)
{
// ERROR RETURN TO ME BELOW !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
calcType.GetProperty(PropertyName).SetValue(instance, Vals[0], null);
}
foreach (string PropertyName in PropertyNames)
{
Console.WriteLine(calcType.GetProperty(PropertyName).GetValue(instance, null).ToString());
}
}
}
public class GetData
{
public IEnumerable<Table> GetVals()
{
List<Table> list = new List<Table>();
list.Add(new Table() { ID = 1, Name = "yusuf" });
list.Add(new Table() { ID = 2, Name = "berkay" });
return list;
}
}
public class Table
{
internal int ID { get; set; }
internal string Name { get; set; }
}
public class CTable
{
internal int ID { get; set; }
internal int TID { get; set; }
internal int AnyID { get; set; }
}
public class QTable
{
internal int ID { get; set; }
internal int TID { get; set; }
internal int AnyID { get; set; }
}
public class TTable
{
internal int ID { get; set; }
internal int TID { get; set; }
internal int AnyID { get; set; }
}
}
It looks like the problem is simply that the properties are non-public, so GetProperty("AnyID") etc will return null. To fetch non-public properties, you need binding flags:
calcType.GetProperty(PropertyName, BindingFlags.Instance|BindingFlags.NonPublic)
You might also want to loop at something like dapper which will do the binding for you, and is much faster (it pre-generates IL via the emit API, rather than per-item/per-member reflection).
Related
I am trying to print all the properties name from an object including all the properties name of it's child objects, for example
public class Student
{
public int Id { get; set; }
public List<Subject> Subjects { get; set; }
}
public class Subject
{
public string Code { get; set; }
public ScoreCalculator ScoreCalculator { get; set; }
}
public class ScoreCalculator
{
public double Score { get; set; }
public bool Pass { get; set; }
}
Student is parent object.
Subject is child object of Student.
ScoreCalculator is child object of Subject.
What I expected to see
Id
Code
Score
Pass
I tried below method. It can print parent properties name only.
public static void PrintAllPropertiesNames<T>(T parentObject, int remainingLayersToGo = 3)
{
PropertyDescriptorCollection propertyDescriptorCollection = TypeDescriptor.GetProperties(typeof(T));
foreach (PropertyDescriptor eachPropertyDescriptor in propertyDescriptorCollection)
{
Type type = eachPropertyDescriptor.PropertyType;
//if(remainingLayersToGo > 0 && type is IEnumerable || type is Object) <- #I was trying to recognize data type.
if (remainingLayersToGo > 0 && false)
{
var childObject = eachPropertyDescriptor.GetValue(parentObject);
PrintAllPropertiesNames(childObject, remainingLayersToGo--);
}
else
{
Console.WriteLine(eachPropertyDescriptor.Name);
}
}
}
#This is incorrect. I don't know how to recognize for object(Subject,ScoreCalculator), enumerations(array,list) and normal data type (string, int & double)
I think reflection is the tool of choice for this problem. With reflection it is possible to examine every little detail of an object. The difficulty is rather examining the right details of an object that lead to the desired goal. And to find the right functionality for it.
The basis for the following example is the source code from your question.
NOTE: The following example is really not in good programming style, it is simply intended to show an approach to inspecting objects with reflection.
First I provided the corresponding classes with constructors so that the dependencies for the entire object are created.
public class Student
{
public Student()
{
var subject1 = new Subject();
List<Subject> Subjects = new List<Subject>();
Subjects.Add(subject1);
}
public int Id { get; set; }
public List<Subject> Subjects { get; set; }
}
public class Subject
{
public Subject()
{
ScoreCalculator = new ScoreCalculator();
}
public string Code { get; set; }
public ScoreCalculator ScoreCalculator { get; set; }
}
public class ScoreCalculator
{
public double Score { get; set; }
public bool Pass { get; set; }
}
Next, the object is created and passed to the method.
public static void Main()
{
var student = new Student();
PrintAllPropertiesNames(student);
}
The method shows only a fraction of the possibilities that are available. But it shows an approach how you could examine your objects.
public static void PrintAllPropertiesNames<T>(T obj)
{
if(obj == null) throw new ArgumentNullException(nameof(obj));
var objType = obj.GetType();
Console.WriteLine($"{objType.Name} Properties:");
var objProperties = objType.GetProperties();
foreach(var property in objProperties)
{
Console.WriteLine($" + {property.Name}");
}
var propertySubjects = objType.GetProperty("Subjects");
Console.WriteLine();
Console.WriteLine($"{propertySubjects.Name} Info:");
Console.WriteLine($" + Type: {propertySubjects.PropertyType}");
var subjectsArguments = propertySubjects.PropertyType.GetGenericArguments();
foreach(var argument in subjectsArguments)
{
Console.WriteLine($" + Generic Argument: {argument.Name}");
}
Console.WriteLine();
var subjectType = subjectsArguments[0];
var subjectProperties = subjectType.GetProperties();
Console.WriteLine($"{subjectType.Name} Properties:");
foreach(var property in subjectProperties)
{
Console.WriteLine($" + {property.Name}");
}
Console.WriteLine();
var scoreCalculater = subjectType.GetProperty("ScoreCalculator");
Console.WriteLine($"{scoreCalculater.Name} Properties:");
var scoreCalculatorProperties = scoreCalculater.PropertyType.GetProperties();
foreach(var property in scoreCalculatorProperties)
{
Console.WriteLine($" + {property.Name}");
}
}
The example produces the following output.
Student Properties:
+ Id
+ Subjects
Subjects Info:
+ Type: System.Collections.Generic.List`1[HelloWold.Subject]
+ Generic Argument: Subject
Subject Properties:
+ Code
+ ScoreCalculator
ScoreCalculator Properties:
+ Score
+ Pass
I can recommend the MSDN to go deeper into the topic:
Reflection inC#
I hope I could help you with this.
I'm working on a c# application and trying to make this code working. It fails when it tries to copy a property which is a list of a subclass object.
I'm able to check the common properties in both classes and the code works fine for most types I've used, even List<string>. When I have properties of type e.g. List<SubClass>it fails. The code fails also for simple objects of the Subclass.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Questions
{
class Program
{
static void Main(string[] args)
{
Class1 class1 = new Class1
{
Prop1 = "X",
List1 = new List<Class1.SubClass>
{
new Class1.SubClass { SubProp1 = "A", SubProp2 = "B" }
}
};
Class2 class2 = class1.CopyTo<Class2>(); //---> fails when it tries to SetValue for the List<SubClass>
}
}
public class Class1
{
public string Prop1 { get; set; }
public List<SubClass> List1 { get; set; }
public string Prop3 { get; set; }
public class SubClass
{
public string SubProp1 { get; set; }
public string SubProp2 { get; set; }
}
}
public class Class2
{
public string Prop1 { get; set; }
public List<SubClass> List1 { get; set; }
public string Prop4 { get; set; }
public class SubClass
{
public string SubProp1 { get; set; }
public string SubProp2 { get; set; }
}
}
public static class Extensions
{
public static T CopyTo<T>(this Object sourceObject)
{
Type sourceObjectType = sourceObject.GetType();
Type targetType = typeof(T);
var targetInstance = Activator.CreateInstance(targetType, false);
List<PropertyInfo> identicalProperties = new List<PropertyInfo>();
var propertiesTarget = typeof(T).GetProperties();
var propertiesSource = sourceObject.GetType().GetProperties();
foreach (var s_property in propertiesSource)
{
foreach (var t_property in propertiesTarget)
{
if (s_property.Name.Equals(t_property.Name))
{
identicalProperties.Add(s_property);
continue;
}
}
}
object value;
foreach (PropertyInfo property in propertiesTarget)
{
var res = identicalProperties.Any(x=>x.Name.Equals(property.Name));
if (!res)
{
continue;
}
value = sourceObjectType.GetProperty(property.Name).GetValue(sourceObject, null);
property.SetValue(targetInstance, value, null);
}
return (T)targetInstance;
}
}
}
I assume this is achievable but I'm struggling to find a way to identify the type of property and when to cast the value to the correct type here property.SetValue(targetInstance, value, null);. value should probably be casted as a List.
The error thrown by the compiler is:
System.ArgumentException: 'Object of type 'System.Collections.Generic.List1[Questions.Class1+SubClass]' cannot be converted to type 'System.Collections.Generic.List1[Questions.Class2+SubClass]'
Can anyone help? Much appreciated.
In absence of further details or comments I would just like to feedback the final solution I'm adopting. Basically, the use of a outside defined class (instead an internal defined class) will allow the code to work but and provides a practical example how it can work based on this assumption. This is an workaround rather than the solution for my original question.
The example final code as follows:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Questions
{
class Program
{
static void Main(string[] args)
{
Class1 class1 = new Class1
{
Prop1 = "X",
Prop2 = "Y",
List1 = new List<OutsideClass>
{
new OutsideClass { SubProp1 = "A", SubProp2 = "B" },
new OutsideClass { SubProp1 = "C", SubProp2 = "D" },
new OutsideClass { SubProp1 = "E", SubProp2 = "F" }
}
};
Class2 class2 = class1.CopyTo<Class2>();
}
}
public class Class1
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public List<OutsideClass> List1 { get; set; }
public string Prop3 { get; set; }
}
public class Class2
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public List<OutsideClass> List1 { get; set; }
public string Prop4 { get; set; }
}
public class OutsideClass
{
public string SubProp1 { get; set; }
public string SubProp2 { get; set; }
public override string ToString()
{
string text = string.Format("{0} ---> {1}", SubProp1, SubProp2);
return text;
}
}
public static class Extensions
{
public static T CopyTo<T>(this Object sourceObject)
{
Type sourceObjectType = sourceObject.GetType();
Type targetType = typeof(T);
var targetInstance = Activator.CreateInstance(targetType, false);
List<PropertyInfo> identicalProperties = new List<PropertyInfo>();
var propertiesTarget = typeof(T).GetProperties();
var propertiesSource = sourceObject.GetType().GetProperties();
foreach (var s_property in propertiesSource)
{
foreach (var t_property in propertiesTarget)
{
if (s_property.Name.Equals(t_property.Name))
{
identicalProperties.Add(s_property);
continue;
}
}
}
object value;
foreach (PropertyInfo property in propertiesTarget)
{
var res = identicalProperties.Any(x=>x.Name.Equals(property.Name));
if (!res)
{
continue;
}
value = sourceObjectType.GetProperty(property.Name).GetValue(sourceObject, null);
property.SetValue(targetInstance, value, null);
}
return (T)targetInstance;
}
}
}
All common properties will be copied from one class to the other (Prop3 and Prop4 will be disregarded as they only exist in each one of these classes).
I hope this can help anyone trying to do the same.
Please feel free to comment back and provide further details.
Thanks a lot for everyone comments.
Happy new year!
I have a static class in my system keeping track of the frequency of measurements, the number of samples currently read, what sensors are on an which are off and all those nice details.
Now I make a measurement and want to create a report in the report I want to save all the information stored in the static class. something like this :
public static class Details{
public static int samplesRead { get; set;}
public static int frequency { get; set;}
public static List<devices> devices { get; set;}
}
public class Patient{...} // name, surname , blabla
public class ResultsSet {
public DateTime date;
public Patient patient;
public *DetailsObject* details;
}
public void main {
patient p = new patient(...);
... make the measurements ...
var results = new ResultSet();
results.patient = p;
results.DateTime = DateTime.Now();
results.details = **here the magic ** Details.ToObject();
results.Serialize(myFilePath);
}
How can one acomplish that conversion to a single defined object?
it is the capability of making an snapshot of the static class in an object. [...] Just make an object.
So what you could do is to create a DTO that has the same properties as your static class:
public class DetailsSnapshot
{
public int samplesRead { get; set; }
public int frequency { get; set; }
public List<device> devices { get; set; }
}
Not you can map and return such an object at any given time:
public static class Details{
public static int samplesRead { get; set;}
public static int frequency { get; set; }
public static List<device> devices { get; set; }
public static DetailsSnapshot MakeSnapShot()
{
return new DetailsSnapshot
{
samplesRead = samplesRead,
frequency = frequency,
devices = devices.ToList()
};
}
}
You can have then such an snap-shot-object in your results:
public class ResultsSet
{
public DateTime date;
public Patient patient;
public DetailsSnapshot detailsSnapShot;
}
and make the snap shot (here the magic) the following way:
results.detailsSnapShot = Details.MakeSnapShot();
EDIT:
There is also a way using reflection. With this approach you would scan your Details class for the properties and extract the values. You could return a Dictionary which basically maps the names of the properties to the values:
public static Dictionary<string, object> MakeSnapShotReflection()
{
PropertyInfo [] allPorperties = typeof(Details).GetProperties(BindingFlags.Public | BindingFlags.Static);
Dictionary<string, object> valuemapping = new Dictionary<string, object>();
for (int i = 0; i < allPorperties.Length; i++)
{
valuemapping.Add(allPorperties[i].Name, allPorperties[i].GetValue(null));
}
return valuemapping;
}
This way would allow you to extend the Details class with further properties without worrying about extending anything else.
Or the short version:
public static Dictionary<string, object> MakeSnapShotReflection()
{
PropertyInfo[] allPorperties = typeof(Details).GetProperties(BindingFlags.Public | BindingFlags.Static);
return allPorperties.ToDictionary(key => key.Name, value => value.GetValue(null));
}
With this approach you could still use intellisens to access the correct values:
Test Data:
public static class Details
{
public static int samplesRead { get; set;} = 100;
public static int frequency { get; set; } = 2700;
public static List<device> devices { get; set; } = new List<device>()
{
new device { Name = "sensor1" },
new device { Name = "sensor 2" }
};
}
public class device
{
public string Name { get; set; }
}
Test Code to access values:
void Main()
{
Dictionary<string, object> details = Details.MakeSnapShotReflection();
Console.WriteLine(details[nameof(Details.frequency)]);
Console.WriteLine(details[nameof(Details.samplesRead)]);
foreach (var element in details[nameof(Details.devices)] as IEnumerable<device>)
{
Console.WriteLine(element.Name);
}
}
OUTPUT:
2700
100
sensor1
sensor 2
If you want to save and restore it, make it a non-static class and serialise/deserialise it using JSON or XML. You can then go JsonConvert.SerialiseObject and JsonConvert.Deserialise object. Nice and simple.
If you want to ensure only one instance, make the class a singleton.
public class Details
{
private static readonly Details _instance = new Details();
static Details()
{
}
private Details()
{
}
public Details Intance
{
get
{
return _instance;
}
}
public int samplesRead { get; set;}
public int frequency { get; set;}
public List<devices> devices { get; set; }
}
Then you can access it's properties this way:
Details.Instance.samplesRead
If the class has to be static, you can use reflection to serialise it:
public static string SerializeStaticProperties(Type type)
{
var properties = type.GetProperties(BindingFlags.Static | BindingFlags.Public);
var data = new List<Property>();
foreach (var property in properties)
{
data.Add(new Property
{
Name = property.Name,
Type = property.PropertyType,
Value = JsonConvert.SerializeObject(property.GetValue(null))
});
}
return JsonConvert.SerializeObject(data);
}
public static void DeserializeStaticProperties(Type type, string json)
{
var data = JsonConvert.DeserializeObject<List<Property>>(json);
foreach (var item in data)
{
var property = type.GetProperty(item.Name, BindingFlags.Static | BindingFlags.Public);
if (property != null)
{
property.SetValue(null, JsonConvert.DeserializeObject(item.Value, item.Type));
}
}
}
public class Property
{
public string Name { get; set; }
public Type Type { get; set; }
public string Value { get; set; }
}
I have following two classes
public class Family
{
public string ChildName { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public Family Child { get; set; }
}
I have an instance of Employee class as follows.
Employee employee = new Employee();
employee.Name = "Ram";
employee.Id = 77;
employee.Child = new Family() { ChildName = "Lava" };
I have a method which gets the property value based on the property name as follows:
public static object GetPropertyValue(object src, string propName)
{
string[] nameParts = propName.Split('.');
if (nameParts.Length == 1)
{
return src.GetType().GetRuntimeProperty(propName).GetValue(src, null);
}
foreach (String part in nameParts)
{
if (src == null) { return null; }
Type type = src.GetType();
PropertyInfo info = type.GetRuntimeProperty(part);
if (info == null)
{ return null; }
src = info.GetValue(src, null);
}
return src;
}
In the above method,when I try to get property value of nested class like
GetPropertyValue(employee, "employee.Child.ChildName")
or
GetPropertyValue(GetPropertyValue(employee, "Family"), "ChildName"
doesn't return any value because type.GetRuntimeProperty(part) is always null.
Is there any way to fix this problem?
You problem lies in this line:
foreach (String part in nameParts)
Because you are iterating over each part of nameParts, you are also iterating over "employee", which of course is not a valid property.
Try either this:
foreach (String part in nameParts.Skip(1))
Or calling the method like this:
GetPropertyValue(employee, "Child.ChildName")
(Notice no "employee.", because you already pass in an employee)
The problem in this case is that when you split the string employee.Child.ChildName, the "employee" is the first part. However, employee is not a property of the source i.e. Employee Class.
Try this:
public class Program
{
public static void Main()
{
Employee employee = new Employee();
employee.Name = "Ram";
employee.Id = 77;
employee.Child = new Family() { ChildName = "Lava" };
GetPropertyValue(employee, "employee.Child.ChildName");
}
public class Family
{
public string ChildName { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public Family Child { get; set; }
}
public static object GetPropertyValue(object src, string propName)
{
string[] nameParts = propName.Split('.');
if (nameParts.Length == 1)
{
return src.GetType().GetRuntimeProperty(propName).GetValue(src, null);
}
nameParts = nameParts.Skip(1).ToArray();
foreach (String part in nameParts)
{
if (src == null) { return null; }
Type type = src.GetType();
PropertyInfo info = type.GetRuntimeProperty(part);
if (info == null)
{ return null; }
src = info.GetValue(src, null);
}
return src;
}
Here, i have skipped the first part of the string i.e. "employee". However, you can solve the problem by passing Child.ChildName
This question is around 2 years old, but I found a another working solution for you question, which is easy to understand. If you initialize the object in calling calss constructor you can use dot(.) notation to assign or read property. Example -
public class Family{
public string ChildName { get; set; }
}
public class Employee{
public int Id { get; set; }
public string Name { get; set; }
public Family Child { get; set; }
public Employee(){
Child = new Family();
}
}
Employee emp = new Employee();
emp.Family.ChildName = "Nested calss attribute value";
,I have one class in which I have three properties now what I want to do, if in the object if any one of null or empty then I want to remove it from the object below is my code.
public class TestClass
{
public string Name { get; set; }
public int ID { get; set; }
public DateTime? DateTime { get; set; }
public string Address { get; set; }
}
TestClass t=new TestClass();
t.Address="address";
t.ID=132;
t.Name=string.Empty;
t.DateTime=null;
Now here I want the object of TestClass but in that Name and DateTime property should not be their in the object,
is it possible?
Please help me
There's no such concept as removing a property from an individual object. The type decided which properties are present - not individual objects.
In particular, it will always be valid to have a method like this:
public void ShowDateTime(TestClass t)
{
Console.WriteLine(t.DateTme);
}
That code has no way of knowing whether you've wanted to "remove" the DateTime property from the object that t refers to. If the value is null, it will just get that value - that's fine. But you can't remove the property itself.
If you're listing the properties of an object somewhere, you should do the filtering there, instead.
EDIT: Okay, no you've given us some context:
ok I am using Schemaless database so null and empty value also store space in database that's the reason
So in the code you're using which populates that database, just don't set any fields which corresponds to properties with a null value. That's purely a database population concern - not a matter for the object itself.
(I'd also argue that you should consider how much space you'll really save by doing this. Do you really care that much?)
I was bored and got this in LINQPad
void Main()
{
TestClass t=new TestClass();
t.Address="address";
t.ID=132;
t.Name=string.Empty;
t.DateTime=null;
t.Dump();
var ret = t.FixMeUp();
((object)ret).Dump();
}
public static class ReClasser
{
public static dynamic FixMeUp<T>(this T fixMe)
{
var t = fixMe.GetType();
var returnClass = new ExpandoObject() as IDictionary<string, object>;
foreach(var pr in t.GetProperties())
{
var val = pr.GetValue(fixMe);
if(val is string && string.IsNullOrWhiteSpace(val.ToString()))
{
}
else if(val == null)
{
}
else
{
returnClass.Add(pr.Name, val);
}
}
return returnClass;
}
}
public class TestClass
{
public string Name { get; set; }
public int ID { get; set; }
public DateTime? DateTime { get; set; }
public string Address { get; set; }
}
Hereby a 'slightly' more clear and shorter version of the accepted answer.
/// <returns>A dynamic object with only the filled properties of an object</returns>
public static object ConvertToObjectWithoutPropertiesWithNullValues<T>(this T objectToTransform)
{
var type = objectToTransform.GetType();
var returnClass = new ExpandoObject() as IDictionary<string, object>;
foreach (var propertyInfo in type.GetProperties())
{
var value = propertyInfo.GetValue(objectToTransform);
var valueIsNotAString = !(value is string && !string.IsNullOrWhiteSpace(value.ToString()));
if (valueIsNotAString && value != null)
{
returnClass.Add(propertyInfo.Name, value);
}
}
return returnClass;
}
You could take advantage of the dynamic type:
class Program
{
static void Main(string[] args)
{
List<dynamic> list = new List<dynamic>();
dynamic
t1 = new ExpandoObject(),
t2 = new ExpandoObject();
t1.Address = "address1";
t1.ID = 132;
t2.Address = "address2";
t2.ID = 133;
t2.Name = "someName";
t2.DateTime = DateTime.Now;
list.AddRange(new[] { t1, t2 });
// later in your code
list.Select((obj, index) =>
new { index, obj }).ToList().ForEach(item =>
{
Console.WriteLine("Object #{0}", item.index);
((IDictionary<string, object>)item.obj).ToList()
.ForEach(i =>
{
Console.WriteLine("Property: {0} Value: {1}",
i.Key, i.Value);
});
Console.WriteLine();
});
// or maybe generate JSON
var s = JsonSerializer.Create();
var sb=new StringBuilder();
var w=new StringWriter(sb);
var items = list.Select(item =>
{
sb.Clear();
s.Serialize(w, item);
return sb.ToString();
});
items.ToList().ForEach(json =>
{
Console.WriteLine(json);
});
}
}
May be interfaces will be handy:
public interface IAdressAndId
{
int ID { get; set; }
string Address { get; set; }
}
public interface INameAndDate
{
string Name { get; set; }
DateTime? DateTime { get; set; }
}
public class TestClass : IAdressAndId, INameAndDate
{
public string Name { get; set; }
public int ID { get; set; }
public DateTime? DateTime { get; set; }
public string Address { get; set; }
}
Creating object:
IAdressAndId t = new TestClass()
{
Address = "address",
ID = 132,
Name = string.Empty,
DateTime = null
};
Also u can put your interfaces in separate namespace and make your class declaration as internal. After that create some public factories which will create the instances of your classes.