I need a method that will be able to loop through every static property in a static class, and combine these to a string with json syntax, where key name would equal to Property name, and key value would be the value of the property.
So the result would be a string with value:
{ "StaticPropertyName_string": "string_value_of_this_property", "StaticPropertyName_int": 34 }
I have a working method that successfully does the opposite - takes a json and maps data to static fields. But can't figure out how to do it in reverse
public static void MapProfileToJson(JToken source) //// source = plain json string
{
var destinationProperties = typeof(Fields)
.GetProperties(BindingFlags.Public | BindingFlags.Static);
foreach (JProperty prop in source)
{
var destinationProp = destinationProperties
.SingleOrDefault(p => p.Name.Equals(prop.Name, StringComparison.OrdinalIgnoreCase));
var value = ((JValue)prop.Value).Value;
if (typeof(Fields).GetProperty(prop.Name) != null)
destinationProp.SetValue(null, Convert.ChangeType(value, destinationProp.PropertyType));
else
Console.WriteLine("(!) property \"" + prop.Name + "\" is not found in class... skip");
}
}
Example static class:
public static class Fields
{
public static bool BoolField { get; set; }
public static string StringField { get; set; }
public static int IntField { get; set; }
public static double DoubleField { get; set; }
}
p.s.
Key value pairs saved in string, could be wrapped at the end like
ResultString = "{ " + resultString + " }";
C# .NET 4.7.2 (console app)
As others have said, a static class is not a good design here.
However, it is possible to map it back to JSON with some simple reflection:
public static JObject MapStaticClassToJson(Type staticClassToMap)
{
var result = new JObject();
var properties = staticClassToMap.GetProperties(BindingFlags.Public | BindingFlags.Static);
foreach (PropertyInfo prop in properties)
{
result.Add(new JProperty(prop.Name, prop.GetValue(null, null)));
}
return result;
}
I think it would be nice to have some simple code to assign json properties to a static class properties too. I created this code using #RichardDeeming answer
public static void MapStaticClassFromJson(string json, Type staticClassToMap)
{
var jsonObject = JObject.Parse(json);
var properties = staticClassToMap.GetProperties(BindingFlags.Public | BindingFlags.Static);
foreach (PropertyInfo prop in properties)
prop.SetValue(null, Convert.ChangeType(jsonObject[prop.Name], prop.PropertyType, CultureInfo.CurrentCulture), null);
}
test
MapStaticClassFromJson(json,typeof(Data));
Console.WriteLine(Data.StaticPropertyName_int);
Console.WriteLine(Data.StaticPropertyName_string);
result
34
string_value_of_this_property
Related
I have a class with a private List<T> property which I would like to serialize/deserialize using the JsonSerializer. Use of the JsonPropertyAttribute doesn't seem to be supported in .NET Core. So how can I have my private list property serialized?
I'm using System.Text.Json for this.
It seems System.Text.Json does not support private property serialization.
https://learn.microsoft.com/tr-tr/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#internal-and-private-property-setters-and-getters
But as the Microsoft's document says, you can do it with custom converters.
https://www.thinktecture.com/en/asp-net/aspnet-core-3-0-custom-jsonconverter-for-the-new-system_text_json/
Code snippet for serialization & deserialization;
public class Category
{
public Category(List<string> names)
{
this.Names1 = names;
}
private List<string> Names1 { get; set; }
public string Name2 { get; set; }
public string Name3 { get; set; }
}
public class CategoryJsonConverter : JsonConverter<Category>
{
public override Category Read(ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
var name = reader.GetString();
var source = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(name);
var category = new Category(null);
var categoryType = category.GetType();
var categoryProps = categoryType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var s in source.Keys)
{
var categoryProp = categoryProps.FirstOrDefault(x => x.Name == s);
if (categoryProp != null)
{
var value = JsonSerializer.Deserialize(source[s].GetRawText(), categoryProp.PropertyType);
categoryType.InvokeMember(categoryProp.Name,
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty | BindingFlags.Instance,
null,
category,
new object[] { value });
}
}
return category;
}
public override void Write(Utf8JsonWriter writer,
Category value,
JsonSerializerOptions options)
{
var props = value.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.ToDictionary(x => x.Name, x => x.GetValue(value));
var ser = JsonSerializer.Serialize(props);
writer.WriteStringValue(ser);
}
}
static void Main(string[] args)
{
Category category = new Category(new List<string>() { "1" });
category.Name2 = "2";
category.Name3 = "3";
var opt = new JsonSerializerOptions
{
Converters = { new CategoryJsonConverter() },
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
var json = JsonSerializer.Serialize(category, opt);
var obj = JsonSerializer.Deserialize<Category>(json, opt);
Console.WriteLine(json);
Console.ReadKey();
}
Result;
"{\"Names1\":[\"1\"],\"Name2\":\"2\",\"Name3\":\"3\"}"
System.Text.Json supports private property serialization starting with .NET 5 according to Micosoft documentation.
System.Text.Json supports private and internal property setters and getters via the [JsonInclude] attribute. - source
Find more details here.
Although you cannot serialize a private field directly as is, you can do it indirectly.
You need to provide a public property for the field and a constructor as in the following example:
class MyNumbers
{
// This private field will not be serialized
private List<int> _numbers;
// This public property will be serialized
public IEnumerable<int> Numbers => _numbers;
// The serialized property will be recovered with this dedicated constructor
// upon deserialization. Type and name must be the same as the public property.
public MyNumbers(IEnumerable<int> Numbers = null)
{
_numbers = Numbers as List<int> ?? Numbers?.ToList() ?? new();
}
}
The following code demonstrates how that works:
string json;
// Serialization
{
MyNumbers myNumbers = new(new List<int> { 10, 20, 30});
json = JsonSerializer.Serialize(myNumbers);
Console.WriteLine(json);
}
// Deserialization
{
var myNumbers2 = JsonSerializer.Deserialize<MyNumbers>(json);
foreach (var number in myNumbers2.Numbers)
Console.Write(number + " ");
}
Output:
{"Numbers":[10,20,30]}
10 20 30
If you want to detract people from accessing your private data, you can change the name to something explicitly forbidden like __private_numbers.
class MyNumbers2
{
private List<int> _numbers;
public IEnumerable<int> __private_numbers => _numbers;
public MyNumbers2(IEnumerable<int> __private_numbers = null)
{
_numbers = __private_numbers as List<int> ?? __private_numbers?.ToList() ?? new();
}
}
If an external coder is fool enough to access that private data as if it was part of the normal programming interface of that class, then shame on him. You are in your plain right to change that "private interface" without any guilt. And he can't mess with your internal list either, with an IEnumerable.
In most situations, that should be enough.
I've a class "TradingStrategy", with n subclasses ("Strategy1, Strategy2 etc...").
I've a simple UI from which i can choose a subclass (I've got all the subclasses of the "TradingStrategy" class pretty easily).
What i want now is to print (in a datagridview, listbox, combobox, doesn't matter) all the public parameters of the choosen subclass.
I would prefer not to instantiate the subclasses.
namespace BackTester
{
class TradingStrategy
{
public string Name;
}
class MA_Test : TradingStrategy
{
new public string Name = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name;
public int len = 12;
public float lots = 0.1F;
public bool trendFollow = true;
public MA_Test()
{
}
}
class MA_Test2 : TradingStrategy
{
new public string Name = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name;
public int len = 24;
public float lots = 0.1F;
public bool trendFollow = true;
public MA_Test2()
{
}
}
}
With this code i can insert into a combo box every subclass of "TradingStrategy"
var type = typeof(TradingStrategy);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p));
foreach (var t in types){
if (t.Name == "TradingStrategy") continue;
boxStrategy.Items.Add(t.Name);
}
I wanna be able to, from the combobox.Text, get all the properties name and values of the corrisponding subclass.
I think I've read (and tried) every post here and in other forum. Many use reflections.
What is the simplest way to get those prop/values?
Thanks
Why not just create an interface ITradingStrategy:
public interface ITradingStrategy
{
string Name { get; }
int len { get; }
float lots { get; }
bool trendFollow { get; }
}
And have all classes inherit from the interface then pull values from interface.
As was mentioned in the comments, you have to instantiate an instance of the class in order to set some values on it.
To get the public fields/properties and their types without instantiating the objects, you can use reflection as follows:
private static Dictionary<string, Type> GetFields(Type t)
{
var fields = new Dictionary<string, Type>();
foreach (var memberInfo in t.GetMembers(BindingFlags.Instance | BindingFlags.Public))
{
var propertyInfo = memberInfo as PropertyInfo;
var fieldInfo = memberInfo as FieldInfo;
if (propertyInfo != null)
{
fields.Add(propertyInfo.Name, propertyInfo.PropertyType);
}
if (fieldInfo != null)
{
fields.Add(fieldInfo.Name, fieldInfo.FieldType);
}
}
return fields;
}
If you already have the object, you can get all the public fields/values with this method.
private static Dictionary<string, object> GetValues(FileInfo o)
{
var values = new Dictionary<string, object>();
foreach (var memberInfo in o.GetType().GetMembers(BindingFlags.Instance | BindingFlags.Public))
{
var propertyInfo = memberInfo as PropertyInfo;
var fieldInfo = memberInfo as FieldInfo;
if (propertyInfo != null)
{
values.Add(propertyInfo.Name, propertyInfo.GetValue(o, null));
}
if (fieldInfo != null)
{
values.Add(fieldInfo.Name, fieldInfo.GetValue(o));
}
}
return values;
}
The following code is a very slow way to get all the types which derive from a given type, due to the way that the CLR implements GetTypes() and the fact there could be thousands of unrelated types in your code which makes the haystack to search even bigger. The only time you should use this method is if you dynamically load assemblies at runtime containing object definitions that you need to load. Unfortunately there is no other way to get this information at runtime:
var type = typeof(TradingStrategy);
var subtypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => p != type && type.IsAssignableFrom(p));
I would recommend that you store this list of types somewhere in your code, e.g. in an array, and iterate over it when you need to know all of your strategies:
private static readonly Type[] TradingStrategies =
{
typeof(Strategy1),
typeof(Strategy2),
typeof(Strategy3),
};
After reading Erik's answer. If you will never instantiate these classes, you could store this data in a configuration file, and use something like JSON.net to read it, or if you don't want to use an external library, XmlSerializer would work as well. In this case you would store each MATest as a Dictionary (which lends itself nicely to JSON.net's JObject. Using JSON.net, you would have a configuration file that looks like:
[
{
"MA_Test": {
"len": 12,
"lots": 0.1,
"trendFollow": true
},
"MA_Test2": {
"len": 24,
"lots": 0.1,
"trendFollow": true
}
}
]
Then read it with code that looks like:
public JObject ReadConfig(string configPath)
{
using (var filestream = File.Open(configPath, FileMode.Open))
using (var streamReader = new StreamReader(filestream))
using (var jsonTextReader = new JsonTextReader(streamReader))
{
var jsonSerializer = new JsonSerializer();
return jsonSerializer.Deserialize<JObject>(jsonTextReader);
}
}
Thank you all for you answers.
The simplest way I found to get the properties from an indirected instantiated class is this:
var strategy = activator.CreateInstance(Type.GetType("BackTester."+boxStrategy.Text));
foreach (FieldInfo prop in strategy.GetType().GetFields(BindingFlags.Public
| BindingFlags.Instance))
{
listBox1.Items.Add(prop.ToString() + " " + prop.GetValue(strategy));
}
Based on the code you've provided, there is no reason for there to be separate classes for each MA_Test (X DO NOT use underscores, hyphens, or any other nonalphanumeric characters.). Instead these should be the same class with different properties (not fields).
class TradingStrategy
{
public string Name { get; set; }
}
class MATest : TradingStrategy
{
// this is not needed if it is inherited by TradingStragegy
// You should be getting a warning that you are hiding
// the field/property
// public string Name { get; set; }
// Should probably be more descriptive
// e.g. LengthInFeet...
public int Length { get; set; }
public float Lots { get; set; }
// I recommended boolean properties to be prefixed with
// Is, Can, Has, etc
public bool CanTrendFollow { get; set; }
}
// Somewhere Else...
var MATests = new List<MATest>()
{
new MATest()
{
Name = "MATest",
Length = 12,
Lots = 0.1F,
CanTrendFollow = true
},
new MATest()
{
Name = "MATest",
Length = 24,
Lots = 0.1F,
CanTrendFollow = true
},
}
Now instead of costly Reflection and Activator, just create the list classes once (manually, from config or even a database), and they can be used for whatever you need.
When creating a new item; is there any way to access all the field values that are set.
Since I'm using Entity.GetModifiedMembers() method to access the values of the fields that are changed when updating for logging purposes, the purpose is to have the equivalent result through an entity when creating, like a method Entity.GetSetMembers().
So in general, all I need is a key-value pair with "member name" and "value" items.
Example:
public class SomethingEntity
{
public int Id {get;set;}
public string Name {get;set;}
public DateTime Created {get;set;}
public DateTime Modified {get;set;}
}
public Dictionary<string, string> GetFieldsAndValuesOfCreatedItem(object entity)
{
//This is what I need, that can take all the objects from an entity and give
//the property-value pairs for the object instance
return RequieredMethod(entity);
}
public ExampleMethod()
{
var newObject = new SomethingEntity() { Name = "SomeName", Created = DateTime.Now };
Entity.insetOnSubmit(newObject);
Entity.SubmitChanges();
var resultList = GetFieldsAndValuesOfCreatedItem(newObject);
foreach (var propertyKeyValue in resultList)
{
var propertyString = "Property Name: " + propertyKeyValue.Key;
var valueString = "Value : " + propertyKeyValue.Value;
}
}
I've found out that, Reflection is the answer for that as far as I could find: so here is the method I've come up with:
public static Dictionary<string, string> GetFieldsAndValuesOfCreatedItem(object item)
{
var propertyInfoList = item.GetType().GetProperties(BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.Instance);
var list = new Dictionary<string, string>();
foreach (var propertyInfo in propertyInfoList)
{
var valueObject = propertyInfo.GetValue(item, null);
var value = valueObject != null ? valueObject.ToString() : string.Empty;
if (!string.IsNullOrEmpty(value))
{
list.Add(propertyInfo.Name, value);
}
}
return list;
}
Im looking for a way to create CSV from all class instances.
What i want is that i could export ANY class (all of its instances) to CSV.
Can some1 direct me to possible solution for this (in case already anwsered).
thanx !
Have a look at LINQ to CSV. Although it's a little on the heavy side, which is why I wrote the following code to perform just the small subset of functionality that I needed. It handles both properties and fields, like you asked for, although not much else. One thing it does do is properly escape the output in case it contains commas, quotes, or newline characters.
public static class CsvSerializer {
/// <summary>
/// Serialize objects to Comma Separated Value (CSV) format [1].
///
/// Rather than try to serialize arbitrarily complex types with this
/// function, it is better, given type A, to specify a new type, A'.
/// Have the constructor of A' accept an object of type A, then assign
/// the relevant values to appropriately named fields or properties on
/// the A' object.
///
/// [1] http://tools.ietf.org/html/rfc4180
/// </summary>
public static void Serialize<T>(TextWriter output, IEnumerable<T> objects) {
var fields =
from mi in typeof (T).GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
where new [] { MemberTypes.Field, MemberTypes.Property }.Contains(mi.MemberType)
let orderAttr = (ColumnOrderAttribute) Attribute.GetCustomAttribute(mi, typeof (ColumnOrderAttribute))
orderby orderAttr == null ? int.MaxValue : orderAttr.Order, mi.Name
select mi;
output.WriteLine(QuoteRecord(fields.Select(f => f.Name)));
foreach (var record in objects) {
output.WriteLine(QuoteRecord(FormatObject(fields, record)));
}
}
static IEnumerable<string> FormatObject<T>(IEnumerable<MemberInfo> fields, T record) {
foreach (var field in fields) {
if (field is FieldInfo) {
var fi = (FieldInfo) field;
yield return Convert.ToString(fi.GetValue(record));
} else if (field is PropertyInfo) {
var pi = (PropertyInfo) field;
yield return Convert.ToString(pi.GetValue(record, null));
} else {
throw new Exception("Unhandled case.");
}
}
}
const string CsvSeparator = ",";
static string QuoteRecord(IEnumerable<string> record) {
return String.Join(CsvSeparator, record.Select(field => QuoteField(field)).ToArray());
}
static string QuoteField(string field) {
if (String.IsNullOrEmpty(field)) {
return "\"\"";
} else if (field.Contains(CsvSeparator) || field.Contains("\"") || field.Contains("\r") || field.Contains("\n")) {
return String.Format("\"{0}\"", field.Replace("\"", "\"\""));
} else {
return field;
}
}
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class ColumnOrderAttribute : Attribute {
public int Order { get; private set; }
public ColumnOrderAttribute(int order) { Order = order; }
}
}
Actually, something similar has been addressed here:
Best practices for serializing objects to a custom string format for use in an output file
Is this useful to you?
There is a sample that uses reflection to pull out the field names and values and append them to a string.
You can use reflection to traverse all the class properties/fields and write them to CSV.
A better approach would be to define a custom attribute and decorate the members you want to export and only export those attributes.
I am separating my answer into two sections:
The first one is how to export some generic item list into csv, with encoding, headers - (it will build csv data only for specified headers, and will ignore unneeded properties).
public string ExportCsv<T>(IEnumerable<T> items, Dictionary<string, string> headers)
{
string result;
using (TextWriter textWriter = new StreamWriter(myStream, myEncoding))
{
result = this.WriteDataAsCsvWriter<T>(items, textWriter, headers);
}
return result;
}
private string WriteDataAsCsvWriter<T>(IEnumerable<T> items, TextWriter textWriter, Dictionary<string, string> headers)
{
//Add null validation
////print the columns headers
StringBuilder sb = new StringBuilder();
//Headers
foreach (KeyValuePair<string, string> kvp in headers)
{
sb.Append(ToCsv(kvp.Value));
sb.Append(",");
}
sb.Remove(sb.Length - 1, 1);//the last ','
sb.Append(Environment.NewLine);
//the values
foreach (var item in items)
{
try
{
Dictionary<string, string> values = GetPropertiesValues(item, headers);
foreach (var value in values)
{
sb.Append(ToCsv(value.Value));
sb.Append(",");
}
sb.Remove(sb.Length - 1, 1);//the last ','
sb.Append(Environment.NewLine);
}
catch (Exception e1)
{
//do something
}
}
textWriter.Write(sb.ToString());
return sb.ToString();
}
//Help function that encode text to csv:
public static string ToCsv(string input)
{
if (input != null)
{
input = input.Replace("\r\n", string.Empty)
.Replace("\r", string.Empty)
.Replace("\n", string.Empty);
if (input.Contains("\""))
{
input = input.Replace("\"", "\"\"");
}
input = "\"" + input + "\"";
}
return input;
}
This is the most important function, Its extracting the properties values out of (almost) any generic class.
private Dictionary<string, string> GetPropertiesValues(object item, Dictionary<string, string> headers)
{
Dictionary<string, string> values = new Dictionary<string, string>();
if (item == null)
{
return values;
}
//We need to make sure each value is coordinated with the headers, empty string
foreach (var key in headers.Keys)
{
values[key] = String.Empty;
}
Type t = item.GetType();
PropertyInfo[] propertiesInfo = t.GetProperties();
foreach (PropertyInfo propertiyInfo in propertiesInfo)
{
//it not complex: string, int, bool, Enum
if ((propertiyInfo.PropertyType.Module.ScopeName == "CommonLanguageRuntimeLibrary") || propertiyInfo.PropertyType.IsEnum)
{
if (headers.ContainsKey(propertiyInfo.Name))
{
var value = propertiyInfo.GetValue(item, null);
if (value != null)
{
values[propertiyInfo.Name] = value.ToString();
}
}
}
else//It's complex property
{
if (propertiyInfo.GetIndexParameters().Length == 0)
{
Dictionary<string, string> lst = GetPropertiesValues(propertiyInfo.GetValue(item, null), headers);
foreach (var value in lst)
{
if (!string.IsNullOrEmpty(value.Value))
{
values[value.Key] = value.Value;
}
}
}
}
}
return values;
}
Example for GetPropertiesValues:
public MyClass
{
public string Name {get; set;}
public MyEnum Type {get; set;}
public MyClass2 Child {get; set;}
}
public MyClass2
{
public int Age {get; set;}
public DateTime MyDate {get; set;}
}
MyClass myClass = new MyClass()
{
Name = "Bruce",
Type = MyEnum.Sometype,
Child = new MyClass2()
{
Age = 18,
MyDate = DateTime.Now()
}
};
Dictionary<string, string> headers = new Dictionary<string, string>();
headers.Add("Name", "CustomCaption_Name");
headers.Add("Type", "CustomCaption_Type");
headers.Add("Age", "CustomCaption_Age");
GetPropertiesValues(myClass, headers)); // OUTPUT: {{"Name","Bruce"},{"Type","Sometype"},{"Age","18"}}
My answer is based on Michael Kropat's answer from above.
I added two functions to his answer because it didn't want to write straight to file as I still had some further processing to do. Instead I wanted the header information separate to the values so I could put everything back together later.
public static string ToCsvString<T>(T obj)
{
var fields =
from mi in typeof(T).GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
where new[] { MemberTypes.Field, MemberTypes.Property }.Contains(mi.MemberType)
let orderAttr = (ColumnOrderAttribute)Attribute.GetCustomAttribute(mi, typeof(ColumnOrderAttribute))
select mi;
return QuoteRecord(FormatObject(fields, obj));
}
public static string GetCsvHeader<T>(T obj)
{
var fields =
from mi in typeof(T).GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
where new[] { MemberTypes.Field, MemberTypes.Property }.Contains(mi.MemberType)
let orderAttr = (ColumnOrderAttribute)Attribute.GetCustomAttribute(mi, typeof(ColumnOrderAttribute))
select mi;
return QuoteRecord(fields.Select(f => f.Name));
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Finding the Variable Name passed to a Function in C#
public new Dictionary<string, string> Attributes { get; set; }
public string StringAttributes = string.Empty;
public int? MaxLength { get; set; }
public int? Size { get; set; }
public int? Width { get; set; }
public int? Height { get; set; }
protected override void OnInit(EventArgs e) {
Attributes = new Dictionary<string, string>();
Attributes.Add("MaxLength", MaxLength.ToString());
Attributes.Add("Size", Size.ToString());
Attributes.Add("Width", Width.ToString());
Attributes.Add("Height", Height.ToString());
base.OnInit(e);
}
protected override void OnPreRender(EventArgs e) {
if (Attributes != null) {
StringBuilder attributes = new StringBuilder();
foreach (var item in Attributes) {
if (!string.IsNullOrWhiteSpace(item.Value)) {
attributes.Append(item.Key + "=\"" + item.Value + "\" ");
}
}
StringAttributes = attributes.ToString();
}
}
The problem here is, instead of using Attributes.Add("MaxLength", MaxLength.ToString()); and repeat the same process for other properties, could we not just make a function that is also able to add values to the dictionary, where the keys to be added are their variable names?
Say,
public void addAttribute(object variable){
Attributes = new Dictionary<string, string>();
Attributes.Add(variable.Name, variable.Value);
}...
I guess this is also possible to do with reflection, getting all the nullable properties and looping through them then adding each to the dictionary... But for as long as there are any other ways, we would not stick to reflection.
But if reflection is the only choice, then another problem now would be how to get the nullable properties of the class...
Any help would be greatly appreciated. Thank you.
I can't think of way to do it without reflection.
In order to get all the nullable properties you can you similar code to this:
GetType().GetProperties()
.Where(property =>
property.PropertyType.IsGenericType &&
property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
Usage example that fills attributes dictionary:
PropertyInfo[] typeProperties = GetType().GetProperties();
var nullableProperties = typeProperties.Where(property =>
property.PropertyType.IsGenericType &&
property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>));
var attributes = new Dictionary<string, string>();
foreach (var nullableProperty in nullableProperties)
{
object value = nullableProperty.GetValue(this,null);
attributes.Add(nullableProperty.Name, value == null ?
string.Empty : value.ToString());
}
I'm not sure I fully understand your question without more context, but perhaps this is helpful
If the concern is over reflection overhead for multiple invocations:
Cache that information.
try EmitMapper to fill in values
try AutoMapper to fill in the values
If the problem is getting a variable name via strongly typed compilation then you can use
The Member class I saw on a post from Oliver Hhanappi. Examples of its use are here on my blog
Below is my complete solution. I would say your best bet is to use reflection, as what you're asking is sort of a meta-task. As far as how do you know which properties to add, I would suggest defining your own attribute and applying it to the fields/properties that you want to inspect.
Usage:
Dictionary<string, string> attributes = Inspector<MyClass>.Inspect(target);
The reflection in my sample code is executed once per type inspected, as it is executed within the static constructor of my generic Inspect class:
// apply this attribute to any properties or fields that you want added to the attributes dictionary
[AttributeUsage(
AttributeTargets.Property |
AttributeTargets.Field |
AttributeTargets.Class |
AttributeTargets.Struct |
AttributeTargets.Interface,
AllowMultiple = true, Inherited = true)]
public class InspectAttribute : Attribute
{
// optionally specify the member name explicitly, for use on classes, structs, and interfaces
public string MemberName { get; set; }
public InspectAttribute() { }
public InspectAttribute(string memberName)
{
this.MemberName = memberName;
}
}
public class Inspector<T>
{
// Inspector is a generic class, therefore there will be a separate instance of the _InspectActions variable per type
private static List<Action<Dictionary<string, string>, T>> _InspectActions;
static Inspector()
{
_InspectActions = new List<Action<Dictionary<string, string>, T>>();
foreach (MemberInfo m in GetInspectableMembers(typeof(T)))
{
switch (m.MemberType)
{
case MemberTypes.Property:
{
// declare a separate variable for variable scope with anonymous delegate
PropertyInfo member = m as PropertyInfo;
// create an action delegate to add an entry to the attributes dictionary using the property name and value
_InspectActions.Add(
delegate(Dictionary<string, string> attributes, T item)
{
object value = member.GetValue(item, null);
attributes.Add(member.Name, (value == null) ? "[null]" : value.ToString());
});
}
break;
case MemberTypes.Field:
{
// declare a separate variable for variable scope with anonymous delegate
FieldInfo member = m as FieldInfo;
// need to create a separate variable so that delegates do not share the same variable
// create an action delegate to add an entry to the attributes dictionary using the field name and value
_InspectActions.Add(
delegate(Dictionary<string, string> attributes, T item)
{
object value = member.GetValue(item);
attributes.Add(member.Name, (value == null) ? "[null]" : value.ToString());
});
}
break;
default:
// for all other member types, do nothing
break;
}
}
}
private static IEnumerable<MemberInfo> GetInspectableMembers(Type t)
{
// get all instance fields and properties
foreach (MemberInfo member in t.GetMembers(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.GetField | BindingFlags.GetProperty))
{
// check if the current member is decorated with an Inspect attribute
object[] inspectAttributes = member.GetCustomAttributes(typeof(InspectAttribute), true);
if (inspectAttributes != null && inspectAttributes.Length > 0)
{
yield return member;
}
}
// now look for any Inspect attributes defined at the type level
InspectAttribute[] typeLevelInspectAttributes = (InspectAttribute[])t.GetCustomAttributes(typeof(InspectAttribute), true);
if (typeLevelInspectAttributes != null && typeLevelInspectAttributes.Length > 0)
{
foreach (InspectAttribute attribute in typeLevelInspectAttributes)
{
// search for members matching the name provided by the Inspect attribute
MemberInfo[] members = t.GetMember(attribute.MemberName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.FlattenHierarchy);
if (members != null && members.Length > 0)
{
foreach (MemberInfo member in members)
{
yield return member;
}
}
}
}
}
public static Dictionary<string, string> Inspect(T item)
{
// create a new attributes dictionary
Dictionary<string, string> attributes = new Dictionary<string, string>();
foreach (Action<Dictionary<string, string>, T> inspectAction in _InspectActions)
{
// execute each "inspect" action.
// This will execute the delegates we created earlier, causing entries to be added to the dictionary
inspectAction(attributes, item);
}
return attributes;
}
}
public class BasePage
{
public int? SomeValue { get; set; }
}
// example class with properties decorated with the Inspect attribute
[Inspect("SomeValue")] // also inspect the "SomeValue" property from the BasePage class
public class MyPage : BasePage
{
[Inspect]
public int? MaxLength { get; set; }
[Inspect]
public int? Size { get; set; }
[Inspect]
public int? Width { get; set; }
[Inspect]
public int? Height { get; set; }
public string GenerateAttributeString()
{
System.Text.StringBuilder attributes = new System.Text.StringBuilder();
foreach (KeyValuePair<string, string> item in Inspector<MyPage>.Inspect(this))
{
attributes.Append(item.Key + "=\"" + item.Value + "\" ");
}
return attributes.ToString();
}
}
You can use the following function to extract out the public Nullable properties from a class into the format your looking for. It also calls the getter method for the value.
This is using the same reflection use that #Elisha talked about. Also it does a .ToString() call to the value returned by the getter.
IDictionary<string, string> GetProps<T>(T DataObject)
{
if(null == DataObject)
return new Dictionary<string, string>();
var nullableProperties =
from property in typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public)
from accessor in property.GetAccessors(false)
let returnType = accessor.ReturnType
where returnType.IsGenericType
&& returnType.GetGenericTypeDefinition() == typeof(Nullable<>)
&& accessor.GetParameters().Length == 0
select new { Name=property.Name, Getter=accessor};
return nullableProperties.ToDictionary(
x => x.Name,
x => x.Getter.Invoke(DataObject, null).ToString());
}