say there is a class like
class phones
{
public int Id {get; set;}
public string Name {get; set;}
public string Color {get; set;}
public decimal Price {get; set;}
}
List<phones> myList = GetData();
//list is filled with objects
Now, I know the Id and the exact name of the object's property and want to get the value from the matching object.
private string GetValue(int pid, string featurename)
{
string val = "";
foreach(phones obj in myList)
{
if(obj.Id == pid)
{
//if featurename is 'Name', it should be
//val = obj.Name;
//if featurename is 'Price', it should return
//val = obj.Price;
break;
}
}
return val;
}
Is this possible. Please advise.
Use this:
Phones phones= new Phones();
string returnValue = phones.GetType().GetProperty(featureName).GetValue(phones, null).ToString();
Also, remember to add validation for input featureName and error handling.
How about this:
foreach(phones obj in myList)
{
if(obj.Id == pid)
{
if (featurename == "Name")
{
return obj.Name;
}
else if (featurename == "Price")
{
return obj.Price.ToString();
}
else
{
return string.Empty;
}
}
}
I think you want Property with given featurename and use it
you could use lambda expression like this
or
Use PropertyInfo like this
foreach (PropertyInfo p in typeof(ClassName).GetProperties())
{
string propertyName = p.Name;
//....
}
Related
I have a class as below
public class Details
{
public string CreatedAt {set;get;)
public Order Order { get; set; }
public Customer Customer { get; set; }
}
public class Customer
{
public string Name { get; set; }
public CustomerAddress Address { get; set; }
}
public class CustomerAddress
{
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
public string State { get; set; }
}
and I have HTML file with HTML content and few placeholder. I am replacing placeholders as below.
public static string ReplaceStringPlaceHolders(User Details)
{
string MyHTML= File.ReadAllText(#"...Path");
//Replacing one by one
string newstring= MyHTML.
.Replace("{created_at}", Details.CreatedAt)
.Replace("{customer_info.address.line_1}", Details.Customer.Address.Line1)
.Replace("{customer_info.address.line_2}", Details.Customer.Address.Line2)
.Replace("{customer_info.address.city}", Details.Customer.Address.City)
.Replace("{customer_info.address.state}", Details.Customer.Address.State)
.Replace("{customer_info.address.postal_code}", Details.Customer.Address.PostalCode)
.Replace("{customer_info.address.country}", Details.Customer.Address.Country)
return newstring;
}
but I don't like this way as I have put 50+ placeholders in my HTML file.
Is there a way that we can replace the placeholder when the placeholder name matches to class properties.
I am looking for something like this if possible:
MyHTML.replaceifPlaceHolderMatchesWithClassProperties(Label);
Kindly suggest.
Yes, you can read properties with a help of Reflection and Linq:
using System.Linq;
using System.Reflection;
....
private static string TryReadReflectionValue(object data, string name) {
if (name == null)
return null;
foreach (string item in name.Replace("_", "").Trim('{', '}').Split('.')) {
if (data == null)
return null;
var prop = data
.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Static |
BindingFlags.Public)
.Where(p => p.Name.Equals(item, StringComparison.OrdinalIgnoreCase))
.Where(p => p.CanRead)
.Where(p => p.GetIndexParameters().Length <= 0)
.FirstOrDefault();
if (prop == null)
return null;
data = prop.GetValue(prop.GetGetMethod().IsStatic ? null : data);
}
return data?.ToString();
}
And match placeholders with a help of regular expressions:
using System.Text.RegularExpressions;
...
private static string MyReplace(string text, object data) {
if (text == null)
return text;
return Regex.Replace(
text,
#"\{\w._]+\}",
match => TryReadReflectionValue(data, match.Value) ?? match.Value);
}
Usage:
public static string ReplaceStringPlaceHolders(User Details) {
string MyHTML= File.ReadAllText(#"...Path");
return MyReplace(text, Details);
}
Here I assumed that
We always drop _ in placeholders, e.g {created_at} corresponds to CreatedAt property
We ignore case in placeholders: {created_at} == {Created_At} == {CREATED_at} etc.
All placeholders are in {letters,digits,_,.} format
i think this will help you:
string MyHTML = "{PersonId}, {Name}, {Family}, {Age}";
Person p = new Person();
p.PersonId = 0;
p.Name = "hello";
p.Family = "world";
p.Age = 15;
var properties = typeof(Person).GetProperties().ToDictionary(x => x, x => x.GetType().GetProperties());
foreach (var item in properties)
MyHTML = MyHTML.Replace("{" + item.Key.Name + "}", typeof(Person).GetProperty(item.Key.Name).GetValue(p).ToString());
Console.WriteLine(MyHTML);
output: 0, hello, world, 15
I have the following model that has these fields:
[Key]
public string Id { get; set; }
[IsSearchable]
public string Code{ get; set; }
[IsSearchable]
public string Name { get; set; }
[IsSearchable]
public string Address { get; set; }
[IsSearchable]
public string PostCode { get; set; }
[IsFilterable]
public int? Setting{ get; set; }
[IsFilterable, IsSortable]
public Location Location { get; set; }
I am writing a method to compare whether values in a database match this model. So far it looks like this:
private bool CompareEquality(Index resultBody, Type indexType)
{
var properties = indexType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
List<PropertyInfo> searchableProperties = new List<PropertyInfo>();
List<PropertyInfo> filterableProperties = new List<PropertyInfo>();
List<PropertyInfo> sortableProperties = new List<PropertyInfo>();
if (properties.Count() == resultBody.Fields.Count)
{
foreach (var property in properties)
{
var isSearchableAttribute = property.GetCustomAttribute<IsSearchableAttribute>();
var isFilterableAttribute = property.GetCustomAttribute<IsFilterableAttribute>();
var isSortableAttribute = property.GetCustomAttribute<IsSortableAttribute>();
if (isSearchableAttribute != null)
{
searchableProperties.Add(property);
}
if (isFilterableAttribute != null)
{
filterableProperties.Add(property);
}
if (isSortableAttribute != null)
{
sortableProperties.Add(property);
}
}
CheckAttributeEquality(searchableProperties, filterableProperties, sortableProperties);
}
return false;
}
The CheckAttributeEquality method:
private bool CheckAttributeEquality(List<PropertyInfo> searchableProperties, List<PropertyInfo> filterableProperties, List<PropertyInfo> sortableProperties)
{
if (searchableProperties.Count == 4 && filterableProperties.Count == 2 && sortableProperties.Count == 1)
{
CheckPropertyFields(searchableProperties, filterableProperties, sortableProperties);
return true;
}
return false;
}
As I started to write a method to check that the field names match, like so:
foreach (var property in searchableProperties)
{
if (property.Name == "Id" ||)
{
...
}
if (property.Name == "Code")
{
...
}
// etc
I realised how messy and long-winded this whole approach is. I am not hugely experienced in C# and would appreciate any advice as to how I can refactor this up a little bit? I want to check for attribute and name matches.
you could use the Typedescriptor (using System.ComponentModel) for that. Try this:
var pdc = TypeDescriptor.GetProperties( this ); //or your model object, if its not "this"
foreach (var property in searchableProperties)
{
var descriptors = pdc[ property.Name ];
// check if your searchable descriptor is there, and do error handling
}
Once it works, you could also try to solve it with LINQ.
I have recently written a simple method on a class that does a DeepCopy of all properties and returns the new property.
Below are three sample classes and the DeepCopy method:
class Person
{
public int Age {get; set;}
public string Name {get; set;}
Public Address address {get; set;}
public List<Person> Children {get; set;}
}
class Address
{
public string StreetAddress {get; set;}
public int ZipCode {get; set; }
public Region region {get; set;}
}
class Region
{
public string City {get; set;}
public string Country {get; set;}
}
public static Person DeepCopy(this Person p)
{
return new Person
{
Age = p.Age,
Name = p.Name,
Address = p.Address,
Children = p.Children
}
}
I want to write unit tests, to test:
All the properties of the source object are copied to the second object and the values are identical.
Using reflection, get the list of the properties recursively (since each object can have a property which is another type which itself has a property...) and make sure the DeepCopy method copies all the properties. The reason for this test is to fail, if a new property is added to the Person class and not copied to the new object in DeepCopy method.
I have already tried using reflection to get all the properties, but one of the problems I have is at some point, it gets stuck in a loop.
For unit tests there is lib FluentAssetions:
string username = "dennis";
username.Should().Be("jonas");
Expected username to be "jonas", but "dennis" differs near 'd' (index 0).
As #Backs said, FluentAssertions is what you need here:
To assert that two objects are equal (through their implementation of Object.Equals), use
string otherObject = "whatever";
theObject.Should().Be(otherObject, "because they have the same values");
theObject.Should().NotBe(otherObject);
Try this:
// Taken from: https://gist.github.com/jonathanconway/3330614
public static bool IsSimpleType(this Type type)
{
return
type.IsValueType ||
type.IsPrimitive ||
new Type[] {
typeof(String),
typeof(Decimal),
typeof(DateTime),
typeof(DateTimeOffset),
typeof(TimeSpan),
typeof(Guid)
}.Contains(type) ||
Convert.GetTypeCode(type) != TypeCode.Object;
}
public static bool CompareIEnumerable(IEnumerable enumerable1, IEnumerable enumerable2)
{
var obj1Iterator = enumerable1.GetEnumerator();
var obj2Iterator = enumerable2.GetEnumerator();
bool has1 = obj1Iterator.MoveNext(), has2 = obj2Iterator.MoveNext();
//loop through the enumerables
while (has1 && has2)
{
//compare the values deeply
if (!DeepCompare(obj1Iterator.Current, obj2Iterator.Current))
{
return false;
}
has1 = obj1Iterator.MoveNext();
has2 = obj2Iterator.MoveNext();
}
// if the loop terminated and has1 != has2, one of them have more items, the are not equal
return has1 == has2;
}
public static bool DeepEquals<T>(this T obj1, T obj2)
{
//if one is null and the other is not, they are not equal
if (obj1 != null ^ obj2 != null)
{
return false;
}
//else if both are null, they are equal
else if (obj1 == null && obj2 == null)
{
return true;
}
Type objectsType = obj1.GetType();
//if they are a simple type, compare them using .Equals method
if (objectsType.IsSimpleType())
{
return obj1.Equals(obj2);
}
//if they are IEnumerable type, compare them using CompareIEnumerable method
else if (objectsType.GetInterface("IEnumerable") != null)
{
return CompareIEnumerable((IEnumerable)obj1, (IEnumerable)obj2);
}
//The type is not simple nor IEnumerable, loop through the properties and check if they are equal (Deeply)
foreach (var member in objectsType.GetMembers().Where(m => m.MemberType.Equals(MemberTypes.Field) || m.MemberType.Equals(MemberTypes.Property)))
{
Type t = member.MemberType.Equals(MemberTypes.Field) ?
((FieldInfo)member).FieldType :
((PropertyInfo)member).PropertyType;
Func<object, object> getter = member.MemberType.Equals(MemberTypes.Field) ?
new Func<object, object>(((FieldInfo)member).GetValue) :
new Func<object, object>(((PropertyInfo)member).GetValue);
if (!DeepCompare(getter(obj1), getter(obj2)))
{
return false;
}
}
return true;
}
I'm trying to loop through a DetailClass objects inside a List using reflection just like for string fields, but I can't figure out how.
class DetailClass
{
public string FieldDetail1 { get; set; }
public string FieldDetail2 { get; set; }
public string FieldDetail3 { get; set; }
}
class SomeClass
{
public string Field1 { get; set; }
public string Field2 { get; set; }
public string Field3 { get; set; }
public List<DetailClass> Artikli { get; set; }
}
private static PropertyInfo[] GetProperties(object obj)
{
return obj.GetType().GetProperties();
}
var myData = new SomeClass();
var prop = GetProperties(myData);
foreach (var item in prop)
{
if (item.PropertyType == typeof(string))
{
var name = item.Name,
var value = item.GetValue(myData).ToString()));
}
//how to get name and value for data inside List<DetailClass>?
}
You were trying to enumerate properties of the parent class
GetValue needs the a reference to the class you are dealing with
Code
var myData = new SomeClass();
myData.Artikli = new List<DetailClass>() { new DetailClass() { FieldDetail1 = "asd", FieldDetail2 = "sdfd", FieldDetail3 = "sdfsg" } };
foreach (var obj in myData.Artikli)
{
foreach (var item in obj.GetType().GetProperties())
{
if (item.PropertyType == typeof(string))
{
var name = item.Name;
var val = item.GetValue(obj);
Console.WriteLine(name + ", " + val);
}
}
}
Demo Here
Additional Resources
PropertyInfo.GetValue Method (Object)
Returns the property value of a specified object.
Parameters
obj
Type: System.Object
The object whose property value will be returned.
You can use your method recursively to get inside all layer of properties
You can check if
item.PropertyType.GetInterfaces().Contains(typeof(IEnumerable))
and if true cast (IEnumerable)item.GetValue(myData) and iterate on the result
recursively.
Just like TheDude answered, you can use a recursive method like so;
private void Recursion(object obj)
{
var props = GetProperties(obj);
foreach (var item in props)
{
if (item.PropertyType == typeof(string))
{
var name = item.Name;
var value = item.GetValue(obj)?.ToString();
}
else if (item.PropertyType == typeof(List<DetailClass>))
{
var test = (List<DetailClass>) item.GetValue(obj);
foreach (var t in test)
{
Recursion(t);
}
}
}
}
And do whatever you want with the name and values in the list.
I have a class, which is created and populated from an xml string, I've simplified it for example purposes:
[XmlRoot("Person")]
public sealed class Person
{
[XmlElement("Name")]
public string Name { get; set; }
[XmlElement("Location")]
public string Location { get; set; }
[XmlElement("Emails", Type = typeof(PersonEmails)]
public PersonEmails Emails { get; set; }
}
public class PersonEmails
{
[XmlElement("Email", Type = typeof(PersonEmail))]
public PersonEmail[] Emails { get; set; }
}
public class PersonEmail
{
[XmlAttribute("Type")]
public string Type { get; set; }
[XmlText]
public string Value { get; set; }
}
To extract the information, I'm trying to load them into another class, which is simply:
public class TransferObject
{
public string Name { get; set; }
public ObjectField[] Fields { get; set; }
}
public class ObjectField
{
public string Name { get; set; }
public string Value { get; set; }
}
I'm only populating "Fields" from the other object, which would simply be (Name = "Location", Value = "London"), but for Emails, (Name = "Email"+Type, Value = jeff#here.com)
Currently I can populate all the other fields, but I'm stuck with Emails, and knowing how to dig deep enough to be able to use reflection (or not) to get the information I need. Currently I'm using:
Person person = Person.FromXmlString(xmlString);
List<ObjectField> fields = new List<ObjectField>();
foreach (PropertyInfo pinfo in person.getType().GetProperties()
{
fields.Add(new ObjectField { Name = pinfo.Name, Value = pinfo.getValue(person, null).ToString();
}
How can I expand on the above to add all my emails to the list?
You are trying to type cast a complex values type to string value so you lost the data. Instead use following code:
class Program
{
static void Main(string[] args)
{
Person person = new Person();
person.Name = "Person One";
person.Location = "India";
person.Emails = new PersonEmails();
person.Phones = new PersonPhones();
person.Emails.Emails = new PersonEmail[] { new PersonEmail() { Type = "Official", Value = "xyz#official.com" }, new PersonEmail() { Type = "Personal", Value = "xyz#personal.com" } };
person.Phones.Phones = new PersonPhone[] { new PersonPhone() { Type = "Official", Value = "789-456-1230" }, new PersonPhone() { Type = "Personal", Value = "123-456-7890" } };
List<ObjectField> fields = new List<ObjectField>();
fields = GetPropertyValues(person);
}
static List<ObjectField> GetPropertyValues(object obj)
{
List<ObjectField> propList = new List<ObjectField>();
foreach (PropertyInfo pinfo in obj.GetType().GetProperties())
{
var value = pinfo.GetValue(obj, null);
if (pinfo.PropertyType.IsArray)
{
var arr = value as object[];
for (var i = 0; i < arr.Length; i++)
{
if (arr[i].GetType().IsPrimitive)
{
propList.Add(new ObjectField() { Name = pinfo.Name + i.ToString(), Value = arr[i].ToString() });
}
else
{
var lst = GetPropertyValues(arr[i]);
if (lst != null && lst.Count > 0)
propList.AddRange(lst);
}
}
}
else
{
if (pinfo.PropertyType.IsPrimitive || value.GetType() == typeof(string))
{
propList.Add(new ObjectField() { Name = pinfo.Name, Value = value.ToString() });
}
else
{
var lst = GetPropertyValues(value);
if (lst != null && lst.Count > 0)
propList.AddRange(lst);
}
}
}
return propList;
}
}
Check this snippet out:
if(pinfo.PropertyType.IsArray)
{
// Grab the actual instance of the array.
// We'll have to use it in a few spots.
var array = pinfo.GetValue(personObject);
// Get the length of the array and build an indexArray.
int length = (int)pinfo.PropertyType.GetProperty("Length").GetValue(array);
// Get the "GetValue" method so we can extact the array values
var getValue = findGetValue(pinfo.PropertyType);
// Cycle through each index and use our "getValue" to fetch the value from the array.
for(int i=0; i<length; i++)
fields.Add(new ObjectField { Name = pinfo.Name, Value = getValue.Invoke(array, new object[]{i}).ToString();
}
// Looks for the "GetValue(int index)" MethodInfo.
private static System.Reflection.MethodInfo findGetValue(Type t)
{
return (from mi in t.GetMethods()
where mi.Name == "GetValue"
let parms = mi.GetParameters()
where parms.Length == 1
from p in parms
where p.ParameterType == typeof(int)
select mi).First();
}
You can definately do it with Reflection... You can take advantage of the fact that a Type can tell you if it's an array or not (IsArray)... and then take advantage of the fact that an Array has a method GetValue(int index) that will give you a value back.
Per your comment
Because Emails is a property within a different class, recursion should be used. However the trick is knowing when to go to the next level. Really that is up to you, but
if it were me, I would use some sort of Attribute:
static void fetchProperties(Object instance, List<ObjectField> fields)
{
foreach(var pinfo in instance.GetType().GetProperties())
{
if(pinfo.PropertyType.IsArray)
{
... // Code described above
}
else if(pinfo.PropertyType.GetCustomAttributes(typeof(SomeAttribute), false).Any())
// Go the next level
fetchProperties(pinfo.GetValue(instance), fields);
else
{
... // Do normal code
}
}
}