Can I access a class variable with another variable? - c#

i want to do a class constructor that takes a dicionary as parameter and initialize all the class variables that are listed as key in the dictionary, after of course a type conversion:
public class User
{
public int id;
public string username;
public string password;
public string email;
public int mana_fire;
public int mana_water;
public int mana_earth;
public int mana_life;
public int mana_death;
public User ()
{
}
public User(Dictionary<string,string> dataArray){
FieldInfo[] classVariablesInfoList = typeof(User).GetFields();
for(int i = 0; i < classVariablesInfoList.Length; i++)
{
if(dataArray.ContainsKey(classVariablesInfoList[i].Name)){
//missing code here :)
//need something like classVariable= dataArray["classVariablesInfolist[i].name"]; ?
}
}
}
}
but i can't find out how to do this!
Can you please help? :)

You can use the SetValue frunction from reflection:
FieldInfo f = classVariablesInfoList[i];
if (f.ReflectedType == typeof(int))
{
f.SetValue(this, Convert.ToInt32(dataArray[f.Name]));
}
else
{
f.SetValue(this, dataArray[classVariablesInfoList[i].Name]);
}
But it is a really uncommon way to do this with a dictionary. You should considder accessing the fields directly or add parameters to the constructor for any field. And fields should never be public - use properties instead.

The following will work if Convert.ChangeType() is able to handle the conversion. There are a lot of problems waiting to occur, for example handling numbers or dates where the string representation depends on the locale. I would really suggest to use usual typed constructor parameters or standard (de)serialization mechanism if possible. Or at least use a dictionary containing objects instead of strings to get rid of the conversion, again if possible.
public User(Dictionary<String, String> data)
{
var fields = typeof(User).GetFields();
foreach (field in fields)
{
if (data.ContainsKey(field.Name))
{
var value = Convert.ChangeType(data[field.Name], field.MemberType);
field.SetValue(this, value);
}
}
}

I would like to separate your problem into two parts.
1. Applying conversion
The FieldInfo type present a FieldType property that is the actual type of the field, using this Type we can use the non-generic ChangeType method of System.Convert, this method will be able convert some types to others. Luckily it support String to Int.
Usage:
Convert.ChangeType(OLD_VALUE, TARGET_TYPE);
2. Setting the field
The field info class has a SetValue method (FieldInfo.SetValue), it has two parameters, the first one is the current (ie. this) instance (or the instance you wish to change). the second is the new value you wish to set.
Putting it all together
[...]
var fieldInfo = classVariablesInfoList[i];
var name = fieldInfo.Name;
var targetType = fieldInfo.Type;
var value = Convert.ChangeType(dataArray[name], targetType);
classVariablesInfoList[i].SetValue(this, value);

Related

Perform a 'task' via an enum and a field, by checking the 'T' type, and inferring it into the task automatically

I've come across an issue that I cannot solve. I've got an IReadOnlyList of classes that each have a bunch of fields. These fields have names (variable names) identical to a list of enums. Think that for each field that exists, an enum for it with the exact same name also exists (so object helloHi has an equivalent enum something { helloHi }).
What I've attempted to do is compare the two field names. If they are identical, perform a function on them. The problem is that the function needs to infer a T from the variable, and since reflection isn't able to pull that 'T' without some form of cast, it won't proceed.
This is the code:
public class Something() {
[BackgroundTask]
private void load(Overlay param_1, Config param_2) {
Children = new Drawable[] // is the IReadOnlyList
{
SomethingClass(param_1),
AnotherClass(param_2)
}
performTask(this, param_2);
}
}
public class Config {
public void task<U>(SomeEnums se, ValueType<U> value) // do the task
}
public class SomethingClass {
ValueType<double> someDouble = new ValueType<double>();
ValueType<int> someInt = new ValueType<int>();
public SomethingClass(Overlay overlay) //...
}
public enum SomeEnums {
someDouble,
someInt,
}
void performTask(Something the_class, Config the_config) {
// ... for each field within the_class, do (uses reflection)
field => {
foreach (SomeEnums enums in Enum.GetValues(typeof(SomeEnums)))
{
if (field.Name == enums.ToString()) {
the_config.task(enums, field.GetValue(null)); // cant infer 'U' from an 'object'
}
}
}
}
Technically, I could just do the config.task within the class where the types are known and visible, but I'd much prefer to automate it from here, so that it doesn't need 2-3 changes every time a new variable is created.
One of the strategies I am aware of is performing an if check within the performTask like such:
// performTask, field =>, foreach
{
if (field.FieldType == ValueType<double>)
config.task(enums, (ValueType<double>)field.GetValue(null));
} //etc
However, I don't like this method. It would just need to introduce more and more checks if I ever created more ValueType<> and if they aren't already being checked for. Would there be a better way to perform what I want?
As I mentioned in my comment, I can't quite tell what you really want to do. However, here's some code that should help you figure it out.
It uses reflection to get the fields of objects, look at the names of those fields (comparing them to the values/names associated with an enum type) and compare the values. I do a comparison to integer 5, but you could compare to anything (but, it appears that the integer type's implementation of IComparable.CompareTo throws if it's compared to something other than an int, so I check). Since you know the type of everything, this is easy to check (you don't have to compare to a fixed Type, you can use what is returned by GetType()).
I started with some auxiliary types:
public enum SomeEnums {
SomeDouble,
SomeInt,
}
public class Class1 {
public int SomeInt = 5;
public double SomeDouble = 3.14;
}
public class Class2 {
public int SomeInt = 5;
public double SomeDouble = 6.28;
}
and then added this:
public class EnumFields {
public List<object> Objects = new List<object> {
new Class1(),
new Class2(),
};
public void PerformTask () {
var enumVals = Enum.GetNames(typeof(SomeEnums));
foreach (var obj in Objects) {
var objType = obj.GetType();
var fieldInfos = objType.GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
//right around here I get lost. You have a list of objects (which has two instances right now),
//What are you comparing, that every field named SomeInt has the same value??
//Anyway, here's some code that should help you
foreach (var fieldInfo in fieldInfos) {
if (enumVals.Contains(fieldInfo.Name)) {
var fieldObj = fieldInfo.GetValue(obj);
var isSame = false;
if (fieldObj.GetType() == typeof(int)) {
var comparable = (IComparable)fieldObj;
var same = comparable.CompareTo(5);
isSame = (same == 0);
}
Debug.WriteLine($"Field: {fieldInfo.Name} of instance of {obj.GetType().Name} (Value: {fieldObj}) is equal to 5:{isSame}");
}
}
}
}
}
When I instantiate an EnumFields object and call PerformTask on it, I see this in the output:
Field: SomeInt of instance of Class1 (Value: 5) is equal to 5:True
Field: SomeDouble of instance of Class1 (Value: 3.14) is equal to 5:False
Field: SomeInt of instance of Class2 (Value: 5) is equal to 5:True
Field: SomeDouble of instance of Class2 (Value: 6.28) is equal to 5:False
This should get you most of the way there. I realize it doesn't answer your question. Had I been able to figure out what you were asking, it probably would have.

Can I change this extension method to remove the "magic string"? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Finding the Variable Name passed to a Function in C#
The class below contains the field city.
I need to dynamically determine the field's name as it is typed in the class declaration
i.e. I need to get the string "city" from an instance of the object city.
I have tried to do this by examining its Type in DoSomething() but can't find it when examining the contents of the Type in the debugger.
Is it possible?
public class Person
{
public string city = "New York";
public Person()
{
}
public void DoSomething()
{
Type t = city.GetType();
string field_name = t.SomeUnkownFunction();
//would return the string "city" if it existed!
}
}
Some people in their answers below have asked me why I want to do this.
Here's why.
In my real world situation, there is a custom attribute above city.
[MyCustomAttribute("param1", "param2", etc)]
public string city = "New York";
I need this attribute in other code.
To get the attribute, I use reflection.
And in the reflection code I need to type the string "city"
MyCustomAttribute attr;
Type t = typeof(Person);
foreach (FieldInfo field in t.GetFields())
{
if (field.Name == "city")
{
//do stuff when we find the field that has the attribute we need
}
}
Now this isn't type safe.
If I changed the variable "city" to "workCity" in my field declaration in Person this line would fail unless I knew to update the string
if (field.Name == "workCity")
//I have to make this change in another file for this to still work, yuk!
{
}
So I am trying to find some way to pass the string to this code without physically typing it.
Yes, I could declare it as a string constant in Person (or something like that) but that would still be typing it twice.
Phew! That was tough to explain!!
Thanks
Thanks to all who answered this * a lot*. It sent me on a new path to better understand lambda expressions. And it created a new question.
Maybe you need this. Works fine.
I found this here.
static void Main(string[] args)
{
var domain = "matrix";
Check(() => domain);
Console.ReadLine();
}
static void Check<T>(Expression<Func<T>> expr)
{
var body = ((MemberExpression)expr.Body);
Console.WriteLine("Name is: {0}", body.Member.Name);
Console.WriteLine("Value is: {0}", ((FieldInfo)body.Member)
.GetValue(((ConstantExpression)body.Expression).Value));
}
Output will be:
Name is: 'domain'
Value is: 'matrix'
I know this is old question, but I was trying to achieve the same and google sent me here. After many hours I finally found a way. I hope somebody else will find this useful.
There are actually more ways to accomplish this:
static void Main(string[] args)
{
GetName(new { var1 });
GetName2(() => var1);
GetName3(() => var1);
}
static string GetName<T>(T item) where T : class
{
return typeof(T).GetProperties()[0].Name;
}
static string GetName2<T>(Expression<Func<T>> expr)
{
return ((MemberExpression)expr.Body).Member.Name;
}
static string GetName3<T>(Func<T> expr)
{
return expr.Target.GetType().Module.ResolveField(BitConverter.ToInt32(expr.Method.GetMethodBody().GetILAsByteArray(), 2)).Name;
}
The first is fastest. The last 2 are approx 20 times slower than the 1st one.
http://abdullin.com/journal/2008/12/13/how-to-find-out-variable-or-parameter-name-in-c.html
city in this case is an instance of type string. When you call .GetType() you return the actual string type, which has no knowledge at all of your particular city instance.
I'm having a hard time seeing why you can't just type "city" in the code as a string literal here, if that's what you need. Perhaps it would help if you shared what you want to use this value for and in what circumstances you will call your DoSomething() function.
At the moment, my best guess is that what you really want to do is reflect the entire Person class to get a list of the fields in that class:
public void DoSomething()
{
MemberInfo[] members = this.GetType().GetMembers();
// now you can do whatever you want with each of the members,
// including checking their .Name properties.
}
Okay, based on your edit I have some more for you.
You can find the name of fields that are decorated with your attribute at run-time like this:
Type t = typeof(Person);
foreach (MemberInfo member in t.GetMembers()
.Where(m =>
m.GetCustomAttributes(typeof(MyCustomAttribute)).Any() ) )
{
// "member" is a MemberInfo object for a Peson member that is
// decorated with your attribute
}
You can also use binding flags in the first GetMembers() call to limit it to just fields, if you want.
You mentioned "i.e. I need to get the string "city" from an instance of the object city."
Are you looking to get the field name from the value of the field.
For example:If there are 2 Person object one with city "New York" and the other with city "London", are you looking for the function to return "city". Is this what you mean by dynamic?
With your current design you will always need to compare the name of the field from the FieldInfo against a string.
What if you instead decouple this so that you hold the identifier to use for comparison purposes during reflection as part of the attribute.
Something like this:
public enum ReflectionFields
{
CITY = 0,
STATE,
ZIP,
COUNTRY
}
[AttributeUsage(AttributeTargets.Field,AllowMultiple=false)]
public class CustomFieldAttr : Attribute
{
public ReflectionFields Field { get; private set; }
public string MiscInfo { get; private set; }
public CustomFieldAttr(ReflectionFields field, string miscInfo)
{
Field = field;
MiscInfo = miscInfo;
}
}
public class Person
{
[CustomFieldAttr(ReflectionFields.CITY, "This is the primary city")]
public string _city = "New York";
public Person()
{
}
public Person(string city)
{
_city = city;
}
}
public static class AttributeReader<T> where T:class
{
public static void Read(T t)
{
//get all fields which have the "CustomFieldAttribute applied to it"
var fields = t.GetType().GetFields().Where(f => f.GetCustomAttributes(typeof(CustomFieldAttr), true).Length == 1);
foreach (var field in fields)
{
var attr = field.GetCustomAttributes(typeof(CustomFieldAttr), true).First() as CustomFieldAttr;
if (attr.Field == ReflectionFields.CITY)
{
//You have the field and you know its the City,do whatever processing you need.
Console.WriteLine(field.Name);
}
}
}
}
public class Program
{
public static void Main(string[] args)
{
PPerson p1 = new PPerson("NewYork");
PPerson p2 = new PPerson("London");
AttributeReader<PPerson>.Read(p1);
AttributeReader<PPerson>.Read(p2);
}
}
You can now freely rename _city field of Person to something else and your calling code will still work since the code using reflection is trying to identify the field using the ReflectionFields enum value set as part of initialization of the attribute set on the field.
Yes its possible !!!
Try this out...
public string DoSomething(object city)
{
return city.GetType().GetProperty("Name",typeof(string)).GetValue(city,null);
}
Two things here.
Number one, as someone above pointed out, you're getting the Type for string, not for Person. So typeof(Person).GetMembers() will get you the list of members.
Number two, and more importantly, it looks like you're misunderstanding the purpose of attributes. In general attributes are used to mark a member for specific processing or to add additional information. Here you're using the name to indicate what processing you want, and the attribute to specify parameters, which is mixing of metaphors, or something.
Abhijeet's answer is more appropriate, you mark the field as a city field, then do what you like with it. Where I disagree is that I would use different attribute classes, rather than an enumeration.
Something like:
public class MyAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Field)]
public class MyCityAttribute : MyAttribute
{
}
[AttributeUsage(AttributeTargets.Field]
public class MyNameAttribute: MyAttribute
{
}
public class Person
{
[MyCity]
public string city = "New York";
[MyCity]
public string workCity = "Chicago";
[MyName]
public string fullName = "John Doe";
public Person()
{
}
public void DoSomething()
{
Type t = typeof(Person);
FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.Public);
foreach (var field in fields)
{
MyAttribute[] attributes = field.GetCustomAttributes(typeof(MyAttribute));
if (attributes.Count > 0)
{
if (attributes[0] is MyCityAttribute)
{
//Dosomething for city
break;
}
if (attributes[0] is MyNameAttribute)
{
//Dosomething for names
break;
}
}
}
}
}
This would allow you to use different parameters for MyCity vs MyName that would make more sense in the context of processing each.
I think with your 'yuk' comment above, you hit the nail on the head. That you would have to change a string constant if you rename your variable is an indicator that you're doing something wrong.
t.GetField("city", BindingFlags.Public | BindingFlags.Instance);
or you can call GetFields() to get all fields
You need to call get type on the class Person. The iterate the fields of the class as in the answer below
This is not possible (I think it actually is but involes several hacks and using lambdas). If you want to store attributes about a Person and be able to get the name of the attribute easily, I suggest using a Dictionary<TKey, TValue> from the System.Collections.Generic namespace.
And you can always make public properties that wrap the dictionary.
public class Person
{
Dictionary<string, string> attributes = new Dictionary<string, string();
public string City
{
get { return attributes["city"]; }
set { attributes["city"] = value; }
}
public Person()
{
City = "New York";
}
}
And you can get a list of all attributes with attributes.Keys.
Have a look at this post as it looks similar to what you're trying to do:
Finding the variable name passed to a function
(especially Konrad Rudolph's answer) Another approach could be to just add "city" as one of the parameters in the attribute and fish that out later.
You are already looping through the collection of FieldInfo objects. Look for your attribute on those and when you find the FieldInfo that contains your attribute, you have the one you want. Then call .Name on it.
system.reflection.fieldinfo.attributes

Need to write c# method taking a generic class I create as an argument and parsing out th values of the Properties

I have a number of classes in a project I am working on to simplify working with data that needs to be read in and out of text files, and assigned to data objects in my program. I need to write a method to take any one of these classes and return a csv string of the values. Similarly, I would like to be able to return a csv string of the property names. I could write specific methods for each class, but am trying to do this generically in a way that only have one method to do this.
Example class:
public class Student
{
public string StudentID;
public string Form;
public string ScaleForm;
public double Totalscore;
public double MatrixRawScore;
public double ScaleScore;
public double Theta;
public int PerformanceLevel;
public double LowScaleScore;
public double HighScalescore;
public string AdminID;
public string Group;
public int GroupIndex;
public string ResponseVector;
}
Here is my effort to achieve my goal, but this clearly doesn't work:
public static string ObjectToCsv<T>(T myObject)
{
var fields = typeof(T).GetProperties();
var objectString = "";
foreach (PropertyInfo prop in fields)
{
objectString = prop.GetValue(myObject, null).ToString() + ',';
}
objectString.TrimEnd(',');
return objectString;
}
When my code runs this method, PropertyInfo[] is length 0, there are no PropertyInfos, and so the method returns a blank string. Any thoughts about what I might need to do to make this work?
Code block edited to reflect suggestion below.
I think you are missing the line of code that get the properties(and initialize your fields variable)
typeof(T).GetProperties();
in your case you need to add the below:
var fields = myObject.GetType().GetProperties();
I would make the field in your class autoproperty as showed below:
public class Student
{
public string StudentID { get; set;}
public string Form { get ; set;}
...
}
bear in mind that GetProperties() method has an overload that gets a BindingFlags to add constrains on the returned properties.
You must specify either BindingFlags.Instance or BindingFlags.Static in order to get a return.
Specify BindingFlags.Public to include public properties in the search.
Specify BindingFlags.NonPublic to include non-public properties (that is, private, internal, and protected properties) in the search. Only protected and internal properties on base classes are returned; private properties on base classes are not returned.
Specify BindingFlags.FlattenHierarchy to include public and protected static members up the hierarchy; private static members in inherited classes are not included.
BindingFlags.DeclaredOnly to search only the properties declared on the Type, not properties that were simply inherited
http://msdn.microsoft.com/en-GB/library/kyaxdd3x.aspx
You probably want to do something a little more like this:
public static string ObjectToCsv<T>(T myObject)
{
var objectString = "";
foreach (FieldInfo field in typeof(T).GetFields())
{
objectString += field.GetValue(myObject).ToString() + ',';
}
return objectString.TrimEnd(',');
}
From what you've posted, it looks like you're dealing with fields, not properties. Also, you'll want to += your string instead of setting its' value, otherwise you'll just override it every time you feed it a new field. For some reason TrimEnd() didn't work for me, but I'll leave that out there. Good luck!
Edit: TrimEnd() fixed.

Fill struct with String[]?

I'm parsing a CSV file and placing the data in a struct. I'm using the TextFieldParser from this question and it's working like a charm except that it returns a String[]. Currently I have the ugly process of:
String[] row = parser.ReadFields();
DispatchCall call = new DispatchCall();
if (!int.TryParse(row[0], out call.AccountID)) {
Console.WriteLine("Invalid Row: " + parser.LineNumber);
continue;
}
call.WorkOrder = row[1];
call.Description = row[2];
call.Date = row[3];
call.RequestedDate = row[4];
call.EstStartDate = row[5];
call.CustomerID = row[6];
call.CustomerName = row[7];
call.Caller = row[8];
call.EquipmentID = row[9];
call.Item = row[10];
call.TerritoryDesc = row[11];
call.Technician = row[12];
call.BillCode = row[13];
call.CallType = row[14];
call.Priority = row[15];
call.Status = row[16];
call.Comment = row[17];
call.Street = row[18];
call.City = row[19];
call.State = row[20];
call.Zip = row[21];
call.EquipRemarks = row[22];
call.Contact = row[23];
call.ContactPhone = row[24];
call.Lat = row[25];
call.Lon = row[26];
call.FlagColor = row[27];
call.TextColor = row[28];
call.MarkerName = row[29];
The struct consists of all those fields being Strings except for AccountID being an int. It annoys me that they're not strongly typed, but let's over look that for now. Given that parser.ReadFields() returns a String[] is there a more efficient way to fill a struct (possibly converting some values such as row[0] needing to become an int) with the values in the array?
**EDIT:**One restriction I forgot to mention that may impact what kind of solutions will work is that this struct is [Serializable] and will be sent Tcp somewhere else.
Your mileage may vary on whether it is a better solution, but you could use reflection and define an Attribute class that you use to mark your struct members with. The attribute would take the array index as an argument. Assigning the value from the right array element would then happen by using reflection.
You could define your attribute like this:
[AttributeUsage(AttributeTargets.Property)]
public sealed class ArrayStructFieldAttribute : Attribute
{
public ArrayStructFieldAttribute(int index)
{
this.index = index;
}
private readonly int index;
public int Index {
get {
return index;
}
}
}
This means the attribute can simply be used to associate an int value named Index with a property.
Then, you could mark your properties in the struct with that attribute (just some exemplary lines):
[ArrayStructField(1)]
public string WorkOrder { // ...
[ArrayStructField(19)]
public string City { // ...
The values could then be set with the Type object for your struct type (you can obtain it with the typeof operator):
foreach (PropertyInfo prop in structType.GetProperties()) {
ArrayStructFieldAttribute attr = prop.GetCustomAttributes(typeof(ArrayStructFieldAttribute), false).Cast<ArrayStructFieldAttribute>().FirstOrDefault();
if (attr != null) {
// we have found a property that you want to load from an array element!
if (prop.PropertyType == typeof(string)) {
// the property is a string property, no conversion required
prop.SetValue(boxedStruct, row[attr.Index]);
} else if (prop.PropertyType == typeof(int)) {
// the property is an int property, conversion required
int value;
if (!int.TryParse(row[attr.Index], out value)) {
Console.WriteLine("Invalid Row: " + parser.LineNumber);
} else {
prop.SetValue(boxedStruct, value);
}
}
}
}
This code iterates over all properties of your struct type. For each property, it checks for our custom attribute type defined above. If such an attribute is present, and if the property type is string or int, the value is copied from the respective array index.
I am checking for string and int properties as that's the two data types you mentioned in your question. even though you have only one particular index that contains an int value now, it's good for maintainability if this code is prepared to handle any index as a string or an int property.
Note that for a greater number of types to handle, I'd suggest not using a chain of if and else if, but rather a Dictionary<Type, Func<string, object>> that maps property types to conversion functions.
If you want to create something very flexible you can mark each property on DispatchCall using a custom attribute. Something like this:
class DispatchCall {
[CsvColumn(0)]
public Int32 AccountId { get; set; }
[CsvColumn(1)]
public String WorkOrder { get; set; }
[CsvColumn(3, Format = "yyyy-MM-dd")]
public DateTime Date { get; set; }
}
This allows you to associate each property with a column. For each row you can then iterate over all properties and by using the attribute you can assign the right value to the right property. You will have to do some type conversion from string to numbers, dates and perhaps enums. You can add extra properties to the attribute to assist you in that process. In the example I invented Format which should be used when a DateTime is parsed:
Object ParseValue(String value, TargetType targetType, String format) {
if (targetType == typeof(String))
return value;
if (targetType == typeof(Int32))
return Int32.Parse(value);
if (targetType == typeof(DateTime))
DateTime.ParseExact(value, format, CultureInfo.InvariantCulture);
...
}
Using TryParse methods in the above code can improve the error handling by allowing you to provide more context when an unparsable value is encountered.
Unfortunately, this approach is not very efficient because the reflection code will be executed for each row in your input file. If you want to make this more efficient you need to dynamically create a compiled method by reflecting once over DispatchCall that you then can apply on each row. It is possible but not particular easy.
How dependent are you on the library that you're using? I've found File Helpers to be quite useful for this sort of thing. Your code would look something like:
using FileHelpers;
// ...
[DelimitedRecord(",")]
class DispatchCall {
// Just make sure these are in order
public int AccountID { get; set; }
public string WorkOrder { get; set; }
public string Description { get; set; }
// ...
}
// And then to call the code
var engine = new FileHelperEngine(typeof(DispatchCall));
engine.Options.IgnoreFirstLines = 1; // If you have a header row
DispatchCall[] data = engine.ReadFile(FileName) as DispatchCall[];
You now have a DispatchCall array, and the engine did all the heavy lifting for you.
Use reflection as #Grozz suggested in the comment. Mark each property of the struct class with an attribute (ie [ColumnOrdinal] ) and then use this to map the information with the proper column. If you have double, decimal and so on as a target, you should also consider using Convert.ChangeType to proper convert in the target type. if you are not happy with the performances, you can enjoy create a DynamicMethod on the fly, more challenging, but really performant and beautiful. The challenge is to write the IL instruction in memory to do the "plumbing" you did by hand ( I usually create some example code, and then look inside it with IL spy as a starting point ). of course you will cache somewhere such dynamic methods so creating them is requested just once.
The first thing that comes to mind is to use reflection to iterate over the properties and match them up to the elements in the string[] based on an attribute value.
public struct DispatchCall
{
[MyAttribute(CsvIndex = 1)]
public string WorkOrder { get; set; }
}
MyAttribute would just be a custom attribute with an index that would match up to the field position in the CSV.
var row = parser.ReadFields();
for each property that has MyAttribute...
var indexAttrib = MyAttribute attached to property
property.Value = row[indexAttrib.Index]
next
(Pseudocode, obviously)
or
[StructLayout(LayoutKind.Sequential)] // keep fields in order
public strict DispatchCall
{
public string WorkOrder;
public string Description;
}
StructLayout will keep the struct fields in order, so you can iterate over them without having to explicitly specify a column number for each field. That can save some maintenance if you have a lot of fields.
Or, you could skip the process entirely, and store the field names in a dictionary:
var index = new Dictionary<int, string>();
/// populate index with row index : field name values, preferable from some sort of config file or database
index[0] = "WorkOrder";
index[1] = "Description";
...
var values = new Dictionary<string,object>();
for(var i=0;i<row.Length;i++)
{
values.Add(index[i],row[i]);
}
That's easier to load, but doesn't really take advantage of strong typing, which makes this less than ideal.
You could also generate a dynamic method or a T4 template. You could generate code from a mapping file in the format
0,WorkOrder
1,Description
...
load that, and generate a method that looks like this:
/// emit this
call.WorkOrder = row[0];
call.Description = row[1];
etc.
That approach is used in a few micro-ORMs floating around and seems to work pretty well.
Ideally, your CSV would include a row with field names that would make this a lot easier.
OR, yet another approach, use StructLayout along with a dynamic method to avoid having to keep a field:column_index mapping aside from the struct itself.
OR, create an enum
public enum FieldIndex
{
WorkOrder=0
,
Description // only have to specify explicit value for the first item in the enum
, /// ....
,
MAX /// useful for getting the maximum enum integer value
}
for(var i=0;i<FieldIndex.MAX;i++)
{
var fieldName = ((FieldIndex)i).ToString(); /// get string enum name
var value = row[i];
// use reflection to find the property/field FIELDNAME, and set it's value to VALUE.
}
if you are going for speed you could a brittle switch statement.
var columns = parser.ReadFields();
for (var i = 0; i < columns.Length; i++)
{
SetValue(call, i, columns[i]);
}
private static void SetValue(DispatchCall call, int column, string value)
{
switch column
{
case 0:
SetValue(ref call.AccountId, (value) => int.Parse, value);
return;
case 1:
SetValue(ref call.WorkOrder, (value) => value, value);
return;
...
default:
throw new UnexpectedColumnException();
}
}
private static void SetValue<T>(
ref T property,
Func<string, T> setter
value string)
{
property = setter(value);
}
Its a shame that TextFieldParser does not allow you to read one field at a time, then you could avoid building and indexing the columns array.

Using LINQ to create a List<T> where T : someClass<U>

This is related to a prior question of mine C# Generic List conversion to Class implementing List<T>
I have the following code:
public abstract class DataField
{
public string Name { get; set; }
}
public class DataField<T> : DataField
{
public T Value { get; set; }
}
public static List<DataField> ConvertXML(XMLDocument data) {
result = (from d in XDocument.Parse(data.OuterXML).Root.Decendendants()
select new DataField<string>
{
Name = d.Name.ToString(),
Value = d.Value
}).Cast<DataField>().ToList();
return result;
}
This works however I would like to be able to modify the select portion of the LINQ query to be something like this:
select new DataField<[type defined in attribute of XML Element]>
{
Name = d.Name.ToString(),
Value = d.Value
}
Is this just a poor approach? is it possible? Any suggestions?
Here is a working solution: (You must specify fully qualified type names for your Type attribute otherwise you have to configure a mapping somehow...)
I used the dynamic keyword, you can use reflection to set the value instead if you do not have C# 4...
public static void Test()
{
string xmlData = "<root><Name1 Type=\"System.String\">Value1</Name1><Name2 Type=\"System.Int32\">324</Name2></root>";
List<DataField> dataFieldList = DataField.ConvertXML(xmlData);
Debug.Assert(dataFieldList.Count == 2);
Debug.Assert(dataFieldList[0].GetType() == typeof(DataField<string>));
Debug.Assert(dataFieldList[1].GetType() == typeof(DataField<int>));
}
public abstract class DataField
{
public string Name { get; set; }
/// <summary>
/// Instanciate a generic DataField<T> given an XElement
/// </summary>
public static DataField CreateDataField(XElement element)
{
//Determine the type of element we deal with
string elementTypeName = element.Attribute("Type").Value;
Type elementType = Type.GetType(elementTypeName);
//Instanciate a new Generic element of type: DataField<T>
dynamic dataField = Activator.CreateInstance(typeof(DataField<>).MakeGenericType(elementType));
dataField.Name = element.Name.ToString();
//Convert the inner value to the target element type
dynamic value = Convert.ChangeType(element.Value, elementType);
//Set the value into DataField
dataField.Value = value;
return dataField;
}
/// <summary>
/// Take all the descendant of the root node and creates a DataField for each
/// </summary>
public static List<DataField> ConvertXML(string xmlData)
{
var result = (from d in XDocument.Parse(xmlData).Root.DescendantNodes().OfType<XElement>()
select CreateDataField(d)).ToList();
return result;
}
}
public class DataField<T> : DataField
{
public T Value { get; set; }
}
You cannot do this easily in C#. The generic type argument has to specified at compile time. You can use reflection to do otherwise
int X = 1;
Type listype = typeof(List<>);
Type constructed = listype.MakeGenericType( X.GetType() );
object runtimeList = Activator.CreateInstance(constructed);
Here we have just created a List<int>. You can do it with your type
Different instances of a generic class are actually different classes.
I.e. DataField<string> and DataField<int> are not the same class at all(!)
This means, that you can not define the generic parameter during run-time, as it has to be determined during compile-time.
I would say this is a poor approach. In reality, even after you parse your XML file, you're not going to know what types of "DataFields" you have. You might as well just parse them as objects.
However, if you know that you're only ever going to have x number of types, you can do like so:
var Dictionary<string, Func<string, string, DataField>> myFactoryMaps =
{
{"Type1", (name, value) => { return new DataField<Type1>(name, Type1.Parse(value); } },
{"Type2", (name, value) => { return new DataField<Type2>(name, Type2.Parse(value); } },
};
Termit's answer is certainly excellent. Here is a little variant.
public abstract class DataField
{
public string Name { get; set; }
}
public class DataField<T> : DataField
{
public T Value { get; set; }
public Type GenericType { get { return this.Value.GetType(); } }
}
static Func<XElement , DataField> dfSelector = new Func<XElement , DataField>( e =>
{
string strType = e.Attribute( "type" ).Value;
//if you dont have an attribute type, you could call an extension method to figure out the type (with regex patterns)
//that would only work for struct
Type type = Type.GetType( strType );
dynamic df = Activator.CreateInstance( typeof( DataField<>).MakeGenericType( type ) );
df.Name = e.Attribute( "name" ).Value;
dynamic value = Convert.ChangeType( e.Value , type );
df.Value = value;
return df;
} );
public static List<DataField> ConvertXML( string xmlstring )
{
var result = XDocument.Parse( xmlstring )
.Root.Descendants("object")
.Select( dfSelector )
.ToList();
return result;
}
static void Main( string[] args )
{
string xml = "<root><object name=\"im1\" type=\"System.String\">HelloWorld!</object><object name=\"im2\" type=\"System.Int32\">324</object></root>";
List<DataField> dfs = ConvertXML( xml );
}
you can create generic type by reflection
var instance = Activator.CreateInstance( typeof(DataField)
.MakeGenericType(Type.GetType(typeNameFromAttribute) );
// and here set properties also by reflection
#Termit and #Burnzy put forward good solutions involving factory methods.
The problem with that is that you're loading up your parsing routine with a bunch of extra logic (more testing, more errors) for dubious returns.
Another way to do it would be to use a simplified string-based DataField with typed read methods - the top answer for this question.
An implementation of a typed-value method that would be nice but only works for value types (which does not include strings but does include DateTimes):
public T? TypedValue<T>()
where T : struct
{
try { return (T?) Convert.ChangeType(this.Value, typeof(T)); }
catch { return null; }
}
I'm assuming that you're wanting to use the type information to do things like dynamically assigning user-controls to the field, validation rules, correct SQL types for persistence etc.
I've done a lot of this sort of thing with approaches that seem a bit like yours.
At the end of the day you should seperate your metadata from your code - #Burnzy's answer chooses the code based on the metadata (a "type" attribute of the DataField element) and is a very simple example of this.
If you're dealing with XML, XSDs are a very useful and extensible form of metadata.
As far as what you store each field's data in - use strings because:
they are nullable
they can store partial values
they can store invalid values (makes telling the user to sort their act out more transparent)
they can store lists
special cases won't invade unrelated code because there aren't any
learn regular expressions, validate, be happy
you can convert them to stronger types really easily
I found it very rewarding to develop little frameworks like this - it is a learning experience and you'll come out understanding a lot more about UX and the reality of modelling from it.
There are four groups of test cases that I would advise you to tackle first:
Dates, Times, Timestamps (what I call DateTime), Periods (Timespan)
in particular, make sure you test having a different server locality from the client's.
lists - multi-select foreign keys etc
null values
invalid input - this generally involves retaining the original value
Using strings simplifies all this greatly because it allows you to clearly demarcate responsibilities within your framework. Think about doing fields containing lists in your generic model - it gets hairy rather quickly and it is easy to end up with a special case for lists in pretty much every method. With strings, the buck stops there.
Finally, if you want a solid implementation of this sort of stuff without having to do anything much, consider DataSets - old school I know - they do all sorts of wonderful things you wouldn't expect but you do have to RTFM.
The main downfall of that idea would be that it isn't compatible with WPF data binding - though my experience has been that reality isn't compatible with WPF data binding.
I hope I interpreted your intentions correctly - good luck either way :)
Unfortunately, there no inheritance relation between C<T> and C<string> for instance.
However, you can inherit from a common non-generic class and in addition to this implement a generic interface.
Here I use explicit interface implementation in order to be able to declare a Value property typed as object, as well as a more specifically typed Value property.
The Values are read-only and can only be assigned through a typed constructor parameter. My construction is not perfect, but type safe and doesn't use reflection.
public interface IValue<T>
{
T Value { get; }
}
public abstract class DataField
{
public DataField(string name, object value)
{
Name = name;
Value = value;
}
public string Name { get; private set; }
public object Value { get; private set; }
}
public class StringDataField : DataField, IValue<string>
{
public StringDataField(string name, string value)
: base(name, value)
{
}
string IValue<string>.Value
{
get { return (string)Value; }
}
}
public class IntDataField : DataField, IValue<int>
{
public IntDataField(string name, int value)
: base(name, value)
{
}
int IValue<int>.Value
{
get { return (int)Value; }
}
}
The list can then be declared with the abstract base class DataField as generic parameter:
var list = new List<DataField>();
switch (fieldType) {
case "string":
list.Add(new StringDataField("Item", "Apple"));
break;
case "int":
list.Add(new IntDataField("Count", 12));
break;
}
Access the strongly typed field through the interface:
public void ProcessDataField(DataField field)
{
var stringField = field as IValue<string>;
if (stringField != null) {
string s = stringField.Value;
}
}
While the other questions mostly proposed an elegant solution to convert your XML elements to a generic class instance, I'm going to deal with the consequences of taking the approach to model the DataField class as a generic like DataField<[type defined in attribute of XML Element]>.
After selecting your DataField instance into the list you want to use these fields. Her polymorphism comes into play! You want to iterate your DataFields an treat them in a uniform way. Solutions that use generics often end up in a weird switch/if orgy since there is no easy way to associate behavior based on the generic type in c#.
You might have seen code like this (I'm trying to calculate the sum of all numeric DataField instances)
var list = new List<DataField>()
{
new DataField<int>() {Name = "int", Value = 2},
new DataField<string>() {Name = "string", Value = "stringValue"},
new DataField<float>() {Name = "string", Value = 2f},
};
var sum = 0.0;
foreach (var dataField in list)
{
if (dataField.GetType().IsGenericType)
{
if (dataField.GetType().GetGenericArguments()[0] == typeof(int))
{
sum += ((DataField<int>) dataField).Value;
}
else if (dataField.GetType().GetGenericArguments()[0] == typeof(float))
{
sum += ((DataField<float>)dataField).Value;
}
// ..
}
}
This code is a complete mess!
Let's go try the polymorphic implementation with your generic type DataField and add some method Sum to it that accepts the old some and returns the (possibly modified) new sum:
public class DataField<T> : DataField
{
public T Value { get; set; }
public override double Sum(double sum)
{
if (typeof(T) == typeof(int))
{
return sum + (int)Value; // Cannot really cast here!
}
else if (typeof(T) == typeof(float))
{
return sum + (float)Value; // Cannot really cast here!
}
// ...
return sum;
}
}
You can imagine that your iteration code gets a lot clearer now but you still have this weird switch/if statement in you code. And here comes the point: Generics do not help you here it's the wrong tool at the wrong place. Generics are designed in C# for giving you compile time type safety to avoid potential unsafe cast operations. They additionally add to code readability but that's not the case here :)
Let's take a look at the polymorphic solution:
public abstract class DataField
{
public string Name { get; set; }
public object Value { get; set; }
public abstract double Sum(double sum);
}
public class IntDataField : DataField
{
public override double Sum(double sum)
{
return (int)Value + sum;
}
}
public class FloatDataField : DataField
{
public override double Sum(double sum)
{
return (float)Value + sum;
}
}
I guess you will not need too much fantasy to imagine how much adds to your code's readability/quality.
The last point is how to create instances of these classes. Simply by using some convention TypeName + "DataField" and Activator:
Activator.CreateInstance("assemblyName", typeName);
Short Version:
Generics is not the appropriate approach for your problem because it does not add value to the handling of DataField instances. With the polymorphic approach you can work easily with the instances of DataField!
It's not impossible as you can do this with reflection. But this isn't what generics were designed for and isn't how it should be done. If you're going to use reflection to make the generic type, you may as well not use a generic type at all and just use the following class:
public class DataField
{
public string Name { get; set; }
public object Value { get; set; }
}
You'll need to insert the logic for determining the data type from your XML and add all the types you need to use but this should work:
result = (from d in XDocument.Parse(data.OuterXML).Root.Descendants()
let isString = true //Replace true with your logic to determine if it is a string.
let isInt = false //Replace false with your logic to determine if it is an integer.
let stringValue = isString ? (DataField)new DataField<string>
{
Name = d.Name.ToString(),
Value = d.Value
} : null
let intValue = isInt ? (DataField)new DataField<int>
{
Name = d.Name.ToString(),
Value = Int32.Parse(d.Value)
} : null
select stringValue ?? intValue).ToList();

Categories