Not sure if this is possible, but is there a way to accomplish this?
given this class:
class A
{
public String B {get;set;}
public String C {get;set;}
public String D {get;set;}
}
Instantiate A and assign V1,V2,V3 to B,C,D respectively
I would want a Dictionary\Matrix\some other structure containing
B:V1
C:V2
D:V3
Preferably without using 3rd party libraries
var a = new A{B = "V1", C = "V2", D = "V3"};
var dictionary = a.GetType()
.GetProperties()
.ToDictionary(prop => prop.Name,
prop => prop.GetValue(a));
var props = typeof(A).GetProperties();
Dictionary<string, string> output=new Dictionary<string,string>();
foreach(PropertyInfo pi in props)
{
var name = pi.Name,
string value= pi.GetValue(this, null) as string;
output[name]=value;
}
simple example for fields and its values (for public fields):
var a = new A("a", "b", "c");
var fields = typeof(A).GetFields();
var dict = new Dictionary<string, string>(fields.Length);
foreach (var fieldInfo in fields)
{
dict.Add(fieldInfo.Name, (string)fieldInfo.GetValue(a));
}
Note this is only a sample and you should add more defensive code to check that the types of the fields are correct etc...
a simple way using reflection:
var a = new A();
.
. fill in a.B... etc
.
.
Dictionary<String, String> dict =
a.GetType()
.GetFields()
.ToDictionary(k => k.Name, v => v.GetValue(a).ToString())
GetFields() returns a FieldInfo[] array so you can check for other things first like fields being defined etc.
Related
I have a method that will convert a List<T> into a TSV string with a header row and will only have rows which have a value for any item in the list. What I was doing previously was getting the type of T and working off that. My problem now, it that my list will no longer contain an item of type T, but something that derives from the type of the list. So doing typeof(T).GetProperties() will no longer work because that's looking at the parent type and not the child type.
public static string ListToTSVString<T>(List<T> items)
{
StringBuilder builder = new StringBuilder();
PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
// Get only properties that actually have a value in the list.
var propsDictionary =
properties.ToDictionary(p => p, p => items.Select(l => p.GetValue(l)).ToArray())
.Where(pair => pair.Value.Any(o => o != null))
.ToDictionary(pair => pair.Key, pair => pair.Value);
// Header row
builder.AppendLine(string.Join("\t", propsDictionary.Keys.Select(x => x.GetCustomAttribute<JsonPropertyAttribute>().PropertyName)));
// Body of TSV
foreach (T item in items)
{
builder.AppendLine(string.Join("\t", propsDictionary.Keys.Select(x => x.GetValue(item))));
}
// Remove new line character
return builder.ToString().TrimEnd();
}
As an example of something I am passing in is:
public class CustomObject
{
public string GUID { get; set; }
}
public class Referral : CustomObject
{
public string Name { get; set; }
}
I'd then call this with a: List<CustomObject> objects = new List<CustomObject> { new Referral() { Name = "Jimenemex" } }.
I'm trying to get the TSV string to now contain only the declared properties on the Referral type and not the CustomObject type. Before I was only working with an object that doesn't inherit from anything so this was working nicely.
I've tried to use items.GetType().GetGenericArguments()[0], but that would still get the CustomObject type.
Instead of using typeof(T) you can use the type of item stored in items collections. See following code (use ConcurrentDictionary to cache the properties only)
private static ConcurrentDictionary<Type, PropertyInfo[]> typeProperties = new ConcurrentDictionary<Type, PropertyInfo[]>();
private static PropertyInfo[] GetProperties(Type type)
{
if (!typeProperties.ContainsKey(type))
{
typeProperties[type] = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
}
return typeProperties[type];
}
public static string ListToTSVString<T>(List<T> items)
{
StringBuilder builder = new StringBuilder();
PropertyInfo[] properties = GetProperties(items.First().GetType());
// Get only properties that actually have a value in the list.
var propsDictionary =
properties.ToDictionary(p => p, p => items.Select(l => p.GetValue(l)).ToArray())
.Where(pair => pair.Value.Any(o => o != null))
.ToDictionary(pair => pair.Key, pair => pair.Value);
// Header row
builder.AppendLine(string.Join("\t", propsDictionary.Keys.Select(x => x.GetCustomAttribute<JsonPropertyAttribute>().PropertyName)));
// Body of TSV
foreach (T item in items)
{
builder.AppendLine(string.Join("\t", propsDictionary.Keys.Select(x => x.GetValue(item))));
}
// Remove new line character
return builder.ToString().TrimEnd();
}
You can see this fiddle - https://dotnetfiddle.net/HtnFkG created for demo your scenario.
In my c# code, I have an iteration over a Dictionary and want to achieve something like so using Classes
MyModel othermodel = new MyModel();
Dictionary<string, string> mydictionary = new Dictionary<string, string>()
{
{"n1", "Item"},
{"n2", "Second"},
{"n3", "Third"},
{"n4", "Fourth"},
{"n5", "Fith"},
{"n6", "Sixth"},
{"n7", "Seventh"},
{"n8", "Eighth"},
{"n9", "Ninth"},
{"n0", "Tenth"},
{"n11", "Eleventh"}
};
foreach (var dicitem in mydictionary.ToArray())
{
foreach (MyModel.NewItem.(mydictionary[dicitem].Value) item in othermodel.(mydictionary[dicitem].Key))
{
...
}
}
So my result would be:
first iteration:
foreach (MyModel.NewItem.Item item in othermodel.n1)
{
...
}
second iteration:
foreach (MyModel.NewItem.Second item in othermodel.n2)
{
...
}
...
If there is a way to do this, any help would be appreciated.
Accessing object properties via its names can be done using Reflection, doesn't matter where these names come from (dictionary, array, ...)
Little example here:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
then to access the Name property you do:
var me = new Person {Name = "John", Age = 33};
var propertyName = "Name";
var propertyInfo = typeof(Person).GetProperty(propertyName);
var propertyValue = propertyInfo?.GetValue(me) as string;
Using the upper code, you create one Propertynfo.
If you want to read more properties of the same object, it is better to read all PropertyInfo objects at once:
var me = new Person {Name = "John", Age = 33};
var propertiesInfo = typeof(Person).GetProperties();
var propertyName = "Name";
var nameProperty = propertiesInfo
.Single(p => p.Name == propertyName);
var name = nameProperty.GetValue(me) as string;
//faster approach
var age = (int)propertiesInfo
.Single(p => p.Name == "Age")
.GetValue(me);
Be be aware that in this example, I suppose that the property with specific name exists, so I simply called Single. In different situation however, it may require you to check the existence of property before accessing it.
I have a list of dictionaries which contains student data
It is something like
List<Dictionary<string, object>> students = new List<Dictionary<string, object>>();
Dictionary<string, object> std1 = new Dictionary<string, object>();
std1["name"] = "sai";
std1["age"] = 22;
std1["gender"] = "male";
students.Add(std1);
Dictionary<string, object> std2 = new Dictionary<string, object>();
std2["name"] = "Julia";
std2["gender"] = "female";
students.Add(std2);
Dictionary<string, object> std3 = new Dictionary<string, object>();
std3 ["name"] = "sunny";
std3 ["age"] = 23;
students.Add(std3);
And I want to sort the list of students based on either name, age or gender, I am trying something like this:
var ordered = students.OrderBy(x => x["name"]);
If I try with either age or gender it is returning an error that key is not found, as std2 doesn't have age and std3 doesn't have gender.
I need all the records even it doesn't contain the value for sorted key, Any way to solve this problem, Thanks in advance.
It is better to create a class like this:
public class YourClass
{
public string Name { get; set; }
public int Age { get; set; }
public string Gender { get; set; }
}
Then:
List<YourClass> students = new List<YourClass>();
YourClass std1 = new YourClass();
std1.Name = "sai";
std1.Age = 22;
std1.Gender = "male";
students.Add(std1);
yourClass std2 = new yourClass();
std2.Name = "Julia";
std2.Gender = "female";
students.Add(std2);
yourClass std3 = new yourClass();
std3.Name = "sunny";
std3.Age = 23;
students.Add(std3);
var ordered = students.OrderBy(x => x.Name);
This arrangement stores the same data you had in multiple dictionaries. However, it's far more clear and understandable.
If you want to sort by a key that is not present in all of the dictionaries, you'll need to return a default value instead, for example 0.
var ordered = students.OrderBy(dict =>
{
string name;
if (!dict.TryGetValue("name", out name))
return "";
return name;
});
Shorter version using the conditional ternary operator:
var ordered = students.OrderBy(dict =>
{
string name;
return dict.TryGetValue("name", out name) ? name : 0;
});
I use Dictionary.TryGetValue(...) which returns a bool depicting whether the key was found in the dictionary and its value returned.
You can solve this problem by supplying a GetOptional method that returns some default object in situations when the dictionary does not have a specific key:
V GetOptional<K,V>(IDictionary<K,V> d, K key, V absent) {
V res;
return d.TryGetValue(key, out res) ? res : absent;
}
Now you can sort like this:
var byName = students.OrderBy(x => GetOptional<string,object>(x, "name", "----"));
var byAge = students.OrderBy(x => GetOptional<string,object>(x, "age", "10000"));
Note: Using dictionaries like this gives you flexibility at the expense of clarity. It is usually better to define a special Student type, rather than using a universal collection of key-value pairs.
Is this possible to do in C#?
I have POCO object here is definition:
public class Human
{
public string Name{get;set;}
public int Age{get;set;}
public int Weight{get;set;}
}
I would like to map properties of object Human to string array.
Something like this:
Human hObj = new Human{Name="Xi",Age=16,Weight=50};
Or I can have List<Human>:
string [] props = new string [COUNT OF hObj PROPERTIES];
foreach(var prop in hObj PROPERTIES)
{
props["NAME OF PROPERTIES"] = hObj PROPERTIES VALUE
}
It should be something like this:
var props = new Dictionary<string, object>();
foreach(var prop in hObj.GetType().GetProperties(BindingFlags.Public|BindingFlags.Instance);)
{
props.Add(prop.Name, prop.GetValue(hObj, null));
}
see here for info on GetProperties and here for PropertyInfo
You can use reflection to get an object's properties and values:
var properties = typeof(Human).GetProperties();
IList<KeyValuePair<string, object>> propertyValues = new List<KeyValuePair<string, object>>();
foreach (var propertyInfo in properties)
{
propertyValues.Add(propertyInfo.Name, propertyInfo.GetValue(oneHuman));
}
I have a static class which only contains string properties. I want to convert that class into a name-value pair dictionary with key=PropName, value=PropValue.
Below is the code I have written:
void Main()
{
Dictionary<string, string> items = new Dictionary<string, string>();
var type = typeof(Colors);
var properties = type.GetProperties(BindingFlags.Static);
/*Log properties found*/
/*Iam getting zero*/
Console.WriteLine("properties found: " +properties.Count());
foreach (var item in properties)
{
string name = item.Name;
string colorCode = item.GetValue(null, null).ToString();
items.Add(name, colorCode);
}
/*Log items created*/
Console.WriteLine("Items in dictionary: "+items.Count());
}
public static class Colors
{
public static string Gray1 = "#eeeeee";
public static string Blue = "#0000ff";
}
Output
properties found: 0
Items in dictionary: 0
It's not reading any properties - can anybody tell me what's wrong with my code?
The members in your Colors class are no properties but fields.
Use GetFields in the place of the GetProperties method.
You might end up with something like (also not the change in the call to GetValue):
var properties = type.GetFields(BindingFlags.Static);
/*Log properties found*/
/*Iam getting zero*/
Console.WriteLine("properties found: " +properties.Count());
foreach (var item in properties)
{
string name = item.Name;
string colorCode = item.GetValue(null).ToString();
items.Add(name, colorCode);
}
You can use linq to condense the conversion to a couple of lines:
var type = typeof(Colors);
var fields = type.GetFields().ToDictionary(f => f.Name, f => f.GetValue(f).ToString());
Use this:
var properties = type.GetFields(BindingFlags.Static|BindingFlags.Public);