Generic class check which properties were initialized - c#

Is there a way how I can check from an instantiated class which properties were initially set?
As you can see in the example I can check for "null" value of string data type but I can't check for int value since default value is "0".
Is there a way how I can check if a property was set at "instantiation-time" of the object?
I would like to be able to pass any class to the "ParseProperties" class.
Check this example:
class Program
{
static void Main(string[] args)
{
// The following foreach gives me the output as follows
// Actual output:
// Id
// Name
// Age
//
// Desired output:
// John
foreach (string initiatedPropery in ParseProperties(new Person { Name = "John" }))
{
Console.WriteLine(initiatedPropery);
}
// The following foreach gives me the output as follows
// Actual output:
// Id
// Age
//
// Desired output:
// Id
foreach (string initiatedPropery in ParseProperties(new Person { Id = 45 }))
{
Console.WriteLine(initiatedPropery);
}
Console.ReadLine();
}
private static List<string> ParseProperties<T>(T obj)
{
var initiatedProperties = new List<string>();
var properties = typeof(T).GetProperties();
foreach (var property in properties)
{
// For strings I can check if property is null but I can't check for int's if they were set. How could I do that?
var value = typeof(T).GetProperty(property.Name).GetValue(obj, null);
if (value != null) // --> I would need to get somehow if a property was initially set or not
{
initiatedProperties.Add(property.Name);
}
}
return initiatedProperties;
}
private class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
}

Given a class like this:
private class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
There is really no way to handle this in general that doesn't involve some changes to the class itself. The best you could do without changing the class you are using as the generic type parameter would be something like comparing to myProp == default(S), where S is the type of the property. This would tell you that the property might not have be initialized.
If you can change the classes that are being passed as generic parameters, then you have a lot more options. The simplest would be:
public int? Age { get; set; }
Now the Age property will be null rather than 0.
Another strategy would be to have another property that will tell you if Age was set:
public bool AgeWasSet { get; private set; }
private int _age;
public int Age
{
get { return _age; }
set { _age = value; AgeWasSet = true; }
}
And you could use some convention like propNameWasSet as a property to identify which property is related to which (this isn't unheard of, JSON.Net for example will look for properties with the name ShouldSerializepropName as a way to inject some logic into serialization).
Finally, you could do something like have a base class or an interface that defines a method to give you the information you need. Something like:
public interface IFieldInitializationInfo
{
string[] GetUninitializedFields(); // or maybe PropertyInfo[]
}
And then your classes can implement that interface and report what fields haven't be initialized according to whatever logic you want to use for that particular class.

Is there a way how I can check if a property was set at "instantiation-time" of the object?
Disregarding the use of int? versus int for a "uninitialized" integer, there is no way to tell if values were set in an initializer. An initializer is the equivalent of setting the properties after construction, so
Person p = new Person() {Id = 4};
is exactly the same as
Person p = new Person();
p.Id = 4;
If you require certain properties to be set when the object is constructed, then use a constructor:
public Person(int id)
{
Id = id;
}

Related

Creating an object with a subset of properties and their values from another object

Consider the following class -
public class User
{
[Selected]
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
[Selected]
public int Code { get; set; }
public object GetValue()
{
// need to do something here
}
}
[Selected] here is nothing but a marker attribute. I want GetValue method to return an object which will have the [Selected]-marked properties with corresponding values. That is, in the code below -
private static void Execute()
{
User user = new User
{
Name = "alice",
Email = "alice#liddell.com",
Password = "123456",
Code = 1234
};
var value = user.GetValue();
}
value should be an object with two properties Name and Code which should have the values "alice" and 1234 respectively.
After some searching I tried ExpandoObject (which I never used before) -
public object GetValue()
{
var dictionary = this.GetType().GetProperties().Where(p => p.GetCustomAttribute(typeof(Selected), false) != null).ToDictionary(p => p.Name);
dynamic expando = new ExpandoObject();
foreach (var item in dictionary)
{
object value = item.Value.GetValue(this);
((IDictionary<string, object>)expando).Add(item.Key, value);
}
return expando;
}
But it didn't serve my purpose - the client/consumer of value object somehow couldn't read/access the property values.
Any suggestions?
Edit :
There might be a lot of classes like User and the GetValue method will be called from within a generic method. So, at runtime I have no way to know what type the object is and which properties are marked.
To access the fields by name it is easier to cast the returned object to IDictionary:
var value = (IDictionary<string, object>) user.GetValue();
Console.WriteLine(value["Name"]);
Simplify your method to this:
public Dictionary<string, object> GetValue()
{
var dictionary = this.GetType()
.GetProperties()
.Where(p => p.GetCustomAttribute(typeof(Selected), false) != null)
.ToDictionary(p => p.Name, p => p.GetValue(this));
return dictionary;
}
Use:
var value = user.GetValue(); // value is Dictionary
foreach (var kvp in value)
{
Console.WriteLine(kvp);
}
If you wish POCO, then you can do it like follows
public class User
{
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public int Code { get; set; }
public SelectedUserProperties GetValue()
{
return new SelectedUserProperties
{
Name = Name,
Code = Code
};
}
}
public class SelectedUserProperties
{
public string Name { get; set; }
public int Code { get; set; }
}
It is assumed that the selected properties are known in advance, before compilation.
This makes the marker attribute unnecessary and can be completely removed.
What you did works well, you just have to use the keyword dynamic when you call the method GetValue.
dynamic value = user.GetValue();
If you are using var, value will be of the same type as the return type of your function (i.e object) at compile time. Therefore, if you try to do value.Name your compiler won't allow it because the class Object doesn't have any attribute Name.
dynamic tells your program to do the type checking at runtime.

How to set Object's properties and its value if that Object is a property of Object that is inside of the List using Reflections

I had a similar question before, but this one will need a different solution.
I have object on my Model and object on my service.
I need to set value of Model's object property to a value of properties coming from the service's List<TicketReportPropertyEntity> if both objects' properties are the same.
This is a Model:
public class MyModel{
public ObjectAEntity ObjectAData { get; set; }
public ObjectBEntity ObjectBData { get; set; }
}
ObjectAEntity has a property called "SalesAmount"
This is a service:
public class MyScreenClass
{
public List<TicketReportPropertyEntity> TicketReportPropertyEntities { get; set; }
}
public class TicketReportPropertyEntity
{
public decimal Amount{get;set;}
public ReportPropertyEntity ReportProperty {get;set;}
}
public class ReportPropertyEntity
{
public string ReportGroup { get; set; }
public string PropertyName { get; set; }
}
All the properties, their values and which section(ReportGroup) on the screen they belong to (ObjectAData to the LeftSection and ObjectBData to the RightSection) I'm getting using a reflection from List<TicketReportPropertyEntity> in the following method:
private void SetValues(MyModel m, ObjectAEntity bo, object objectType)
{
string leftSection = "LeftSection";
string rightSection = "RightSection";
m.ObjectAData.SaleAmount = bo.ObjectAData.SaleAmount;
foreach (var ticketReportEntity in mol.TicketReportPropertyEntities)
{
var type = ticketReportEntity.GetType();
PropertyInfo reportProperty = type.GetProperty("ReportProperty");
PropertyInfo reportPropertyName = typeof(ReportPropertyEntity).GetProperty("PropertyName");
PropertyInfo reportPropertyReportGroup = typeof(ReportPropertyEntity).GetProperty("ReportGroup");
PropertyInfo amountProperty = type.GetProperty("Amount");
ReportPropertyEntity reportPropertyValue = (ReportPropertyEntity)reportProperty.GetValue(ticketReportEntity, null);
string reportPropertyNameValue = (string)reportPropertyName.GetValue(reportPropertyValue, null);
decimal value = (decimal)amountProperty.GetValue(ticketReportEntity, null);
//here I need to see if Model's object has the same property as in `ReportProperty` class.
//here I need to find out if the ObjectAEntity has the same property as ReportProperty
if (has)
{
//need to set the value of the Model's `ObjectAEntity` property
}
}
How can I do something like that?
To accomplish this, you would attempt to get the property by the string value stored in the current TicketReportPropertyEntity.ReportPropertyEntity.PropertyName. Since you already have a lot of this setup, it only takes a couple more lines of code.
//here I need to find out if the ObjectAEntity has the same property as ReportProperty
//Attempt to grab the PropertyInfo that you want to set
var objectAEntityReportProperty = bo.GetType().GetProperty(reportPropertyNameValue);
//If it is not null, you have found a match
var has = objectAEntityReportProperty != null;
if (has)
{
//need to set the value of the Model's `ObjectAEntity` property
//Then, set the value
objectAEntityReportProperty.SetValue(bo, ticketReportEntity.Amount);
}

filling object property with string directly

I know how to cast implicity object to string but is it possible to cast string to object property?
I would like to fill object property directly, without using code: ob.property="text".
Instead I would like to use: ob="text"
Example:
class Person
{
public string Name { get; set; }
public string Name2 { get; set; }
public string Name { get; set; }
.
.
.
}
var a=new Person();
I would like to fill property Name with string "text1"
Is there any way to do this : a="text1" instead of a.Name="text1" ?
I know how to cast implicity object to string but is it possible to cast string to object property?
The closest you can achieve this is using implicit operators overload.
For example,
class Person
{
public string Name { get; set; }
public static implicit operator string(Person person)
{
return person.Name;
}
public static implicit operator Person(string name)
{
return new Person(){Name=name};
}
}
Now you can assign as
var person = new Person();
person = "Test Name";
But you would need to evaluate whether this effort is worth it.
While it is a stupid idea, you could use a bit of reflection and as #Bizhan said an indexer to achieve something similar.
static void Main(string[] args)
{
MyClass mc = new MyClass();
mc[0] = "Test Name";
mc[1] = "Test Surname";
}
class MyClass
{
public string this[int i]
{
set
{
var props = this.GetType().GetProperties();
props[i + 1].SetValue(this, value);
}
}
public string Name { get; private set; }
public string Surname { get; private set; }
}
The only problem with this is that it'll only work on String types, the indexer must be first in the class, exception will be thrown when property index will be outside available number of properties, it uses index based property access but that could be automated using a for loop. Then again you could modify this code to do various checks.
It still makes no reason why one would need this - if you explain your use case better there might be better ways to go on about your problem.

System.Reflection.TargetException on employee.GetType().GetProperty(property.Name)

I have come upon an issue I can't seem to figure out. I'm sure there's a simple explanation to this, but I don't understand why I get a System.Reflection.TargetException: 'Object does not match target type' when I try to get a property from (in this case) the employee object.
employee.GetType().GetProperty(property.Name)
Searching for the error returns many results describing problems with calling the Set/GetValue methods, but I haven't found a solution to this one.
I've set a breakpoint where the exception is thrown and it shows that property.Name is indeed a value - and a real property of the object. I've also tried manually specifying a property I know exists. Still the same.
Any suggestions?
EDIT: Tried the following instead:
Type type = typeof (Employee); //Throws the TargetException
PropertyInfo theProperty = type.GetProperty(property.Name);
And now the same exception is thrown at the first line above instead.
EDIT: Added code and more details about the application I'm building.
Class definition for Employee (to simplify mapping to the JSON data this class "represents", the class/fields are in Norwegian - which is the format/language the data comes in, sorry :-).)
"Ansatt" = Employee. "Ansattnummer" = EmployeeNo.
[JsonObject]
public class Ansatt
{
public int Ansattnummer { get; set; }
public string Fornavn { get; set; }
public string Etternavn { get; set; }
public int Pin { get; set; }
public string Adresse { get; set; }
public int Postnummer { get; set; }
public string Poststed { get; set; }
public int TlfPrivat { get; set; }
public int MobilTlf { get; set; }
public string EpostAdresse { get; set; }
public DateTime Fodt { get; set; }
}
My application retrieves a given dataset from a web service - it could be employees, projects or a few other possible datasets. What data to fetch is determined at runtime - by the user. The user can also specify via URL-query which portions, e.g. columns, of the dataset he/she wants. The program then creates a csv-file with the selected data.
Here's the code I use for this:
if (records != null && records.Count != 0) //records contains the chosen dataset - in this case Employees (Ansatt).
{
if (records.GetType() == typeof (List<Ansatt>))
{
foreach (var model in records as List<Ansatt>)
{
var temp = new Ansatt();
foreach (var property in model.GetType().GetProperties())
{
var currentProperty = model.GetType().GetProperty(property.Name);
if (currentProperty != null)
{
Type type = typeof (Ansatt); //Throws System.Reflection.TargetException: 'Object does not match target type'
PropertyInfo tempProperty = type.GetProperty(property.Name);
tempProperty.SetValue(temp, currentProperty.GetValue(property.Name));
}
}
csv.WriteRecord(temp);
}
}
}
You need to specify the name of the property
PropertyInfo value = employee.GetType().GetProperty("Name");
As MSDN goes, you should use it this way:
class MyClass {
private int myProperty;
// Declare MyProperty.
public int MyProperty {
get {
return myProperty;
}
set {
myProperty = value;
}
}
}
public class MyTypeClass {
public static void Main(string[] args) {
try {
// Get the Type object corresponding to MyClass.
Type myType = typeof(MyClass);
// Get the PropertyInfo object by passing the property name.
PropertyInfo myPropInfo = myType.GetProperty("MyProperty");
// Display the property name.
Console.WriteLine("The {0} property exists in MyClass.", myPropInfo.Name);
// Instantiate MyClass
var myObject = new MyClass()
{
MyProperty = 5
};
// Get value using reflection
Console.WriteLine("My property value for my object is {0}.", myPropInfo.GetValue(myObject));
} catch (NullReferenceException e) {
Console.WriteLine("The property does not exist in MyClass." + e.Message);
}
}
}
For your code, when you want to get the property value of an object instance, you should pass object as reference to PropertyInfo.GetValue(object) function.
Instead of this:
tempProperty.SetValue(temp, currentProperty.GetValue(property.Name));
Do this:
tempProperty.SetValue(temp, currentProperty.GetValue(model));
For you to get the property of object through reflection, make it sure that the property name is public with getter and setter else it will return null.
Ex.
public class Employee
{
public string YouProperty { get; set; }
}
var employee = new Employee();
var result = employee.GetType().GetProperty("YouProperty");
// The result is property info
Kindly read some information here.

How to diff Property Values of two objects using GetType GetValue?

I have the following classes:
public class Person
{
public String FirstName { set; get; }
public String LastName { set; get; }
public Role Role { set; get; }
}
public class Role
{
public String Description { set; get; }
public Double Salary { set; get; }
public Boolean HasBonus { set; get; }
}
I want to be able to automatically extract the property value diferences between Person1 and Person2, example as below:
public static List<String> DiffObjectsProperties(T a, T b)
{
List<String> differences = new List<String>();
foreach (var p in a.GetType().GetProperties())
{
var v1 = p.GetValue(a, null);
var v2 = b.GetType().GetProperty(p.Name).GetValue(b, null);
/* What happens if property type is a class e.g. Role???
* How do we extract property values of Role?
* Need to come up a better way than using .Namespace != "System"
*/
if (!v1.GetType()
.Namespace
.Equals("System", StringComparison.OrdinalIgnoreCase))
continue;
//add values to differences List
}
return differences;
}
How can I extract property values of Role in Person???
public static List<String> DiffObjectsProperties(object a, object b)
{
Type type = a.GetType();
List<String> differences = new List<String>();
foreach (PropertyInfo p in type.GetProperties())
{
object aValue = p.GetValue(a, null);
object bValue = p.GetValue(b, null);
if (p.PropertyType.IsPrimitive || p.PropertyType == typeof(string))
{
if (!aValue.Equals(bValue))
differences.Add(
String.Format("{0}:{1}!={2}",p.Name, aValue, bValue)
);
}
else
differences.AddRange(DiffObjectsProperties(aValue, bValue));
}
return differences;
}
If the properties aren't value types, why not just call DiffObjectProperties recursively on them and append the result to the current list? Presumably, you'd need to iterate through them and prepend the name of the property in dot-notation so that you could see what is different -- or it may be enough to know that if the list is non-empty the current properties differ.
Because I don't know how to tell if:
var v1 = p.GetValue(a, null);
is String FirstName or Role Role. I have been trying to find out how to tell if v1 is a String such as FirstName or a class Role. Therefore I won't know when to recursively pass the object property (Role) back to DiffObjectsProperties to iterate its property values.

Categories