Another c# reflection - c#

I have a csv file where each row is a different type of record. I am parsing the csv and want to store the rows (varied types of records) in various types of custom classes.
At each row i need to instantiate a different class based on the record type.
So taken from other reflection examples, I have the code below;
Type type = Type.GetType("myNamespace." + className);
object recordclass = Activator.CreateInstance(type);
so i have an object named recordclass of the correct type, but how do I use it?
all I really want to do is access the properties of the class and populate the row data, and then later add to a container class.
I guess im missing something about the runtime nature of reflection. Please help me connect the dots!
Hope that all makes sense!
TIA,
Gary

With the example you give you could cast your object to the actual type you need:
Type type = Type.GetType("myNamespace." + className);
object recordclass = Activator.CreateInstance(type);
var record = recordClass as ConcreteRecordType;
if(record != null)
record.Name = csv["Name"];
Alternatively look into using a Factory to return populated record objects:
public class RecordFactory
{
RecordBase ParseCsvRow(string[] columns)
{
const int typeDescriminatorColumn = 0;
switch (columns[typeDescriminatorColumn])
{
case "RecordTypeA":
return new RecordTypeA(columns[1], columns[2], ...);
case "RecordTypeB":
return new RecordTypeB(columns[1], columns[2], ...);
default:
throw new InvalidOperationException("Unexpected descriminator: " + columns[typeDescriminatorColumn]);
}
}
}

If you want to store values into your recordclass's property via reflection use this
var property = type.GetProperty(propertyName);
property.SetValue(recordclass,value,null);

If you read the docs, you will see, Type.GetType requires a full qualified type name.

If I understand the problem correctly you have a file which contains text values for records. Each record is stored in a single line, and the start of each line is an identifier to say which kind of record is to be built.
It is possible to use reflection for this but not really neccessary. The problem with using reflection is that you need to know all the properties for the different record types in order to access them by name. At this point, you may as well be working with typed objects but if you are using the a single routine to create all the records (using CreateInstance()) all you have is an untyped object.
Another solution is to have a set of routines each of which take in, say an IEnumerable (the input line split by the comma, excluding the record id) and return an object (or a record, if you have a base record class) and use a factory to select which routine to use for each row.
You register the routines with the factory by some ID (the first field in the record as you are doing is good, it can be the record class name but doesn't have to be) and the iterate through the CSV lines, using the first piece to select the method from the factory and building the record.
Hopefully the example will explain a bit better :? Sorry about the volume of code
The builders in the example just return empty records but populating them from the row pieces should be easy. Another version is to just pass in the row, or a set of rows if a record can cover a number of rows (a but more complicated if the records take in different numbers of rows)
hth,
Alan.
public string[] Input = new[]{
"R1, F1, F2, F3",
"R2, F2, F4",
"R3, F2",
"R3, F2",
"R4, F1, F2, F3, F4"
};
public class RecordOne {
}
public class RecordTwo {
}
public class RecordThree {
}
public class RecordFour {
}
public class BuilderFactory {
public BuilderFactory() {
Builders = new Dictionary<string, Func<IEnumerable<string>, object>>();
}
private Dictionary<string, Func<IEnumerable<string>, object>> Builders { get; set; }
public void RegisterBuilder(string name, Func<IEnumerable<string>, object> builder) {
Builders.Add(name, builder);
}
public Func<IEnumerable<string>, object> GetBuilder(string name) {
return Builders[name];
}
}
[TestMethod]
public void LoadRecords() {
var factory = new BuilderFactory();
factory.RegisterBuilder("R1", BuildRecordOne);
factory.RegisterBuilder("R2", BuildRecordTwo);
factory.RegisterBuilder("R3", BuildRecordThree);
factory.RegisterBuilder("R4", BuildRecordFour);
var output = Input.Select(line => {
var pieces = line.Split(',').Select(val => val.Trim());
var builder = factory.GetBuilder(pieces.First());
return builder(pieces.Skip(1));
});
Assert.IsTrue(new[] {typeof(RecordOne),
typeof(RecordTwo),
typeof(RecordThree),
typeof(RecordThree),
typeof(RecordFour)}.SequenceEqual(output.Select(rec => rec.GetType())));
}
private static RecordOne BuildRecordOne(IEnumerable<string> pieces) {
return new RecordOne();
}
private static RecordTwo BuildRecordTwo(IEnumerable<string> pieces) {
return new RecordTwo();
}
private static RecordThree BuildRecordThree(IEnumerable<string> pieces) {
return new RecordThree();
}
private static RecordFour BuildRecordFour(IEnumerable<string> pieces) {
return new RecordFour();
}

Related

Matching class by enum

Hi I have an abstract class Item. Classes like Food, Weapon, etc inherit by this class. All informations about this items are stored in the database, work of C# Code is match the exact class and match it by Enum which is also stored in the database column as integer. My problem is this stupid code wherever I have to use methods of Food, Weapon etc classes
if ((ItemType)userItem.ItemType == ItemType.Food)
{
Food food = new Food(userItem);
food.UseItem(sender);
}
else if ((ItemType)userItem.ItemType == ItemType.Weapon)
{
Weapon weapon = new Weapon(userItem);
weapon.UseItem(sender);
}
In the parameter of constructor of Food, Weapon etc. classes is the the object from database to let know object about its fields.
Is some kind of stuff that will help me to match this types without this code? It really annoys me when I'm looking at it.
You can use factory or creational method to create specific type of item:
public Item CreateItem(UserItem userItem)
{
var itemType = (ItemType)userItem.ItemType;
switch(itemType)
{
case ItemType.Food: return new Food(userItem);
case ItemType.Weapon: return new Weapon(userItem);
// etc
default:
throw new NotSupportedException($"Item type {itemType} is not supported");
}
}
Then use this method to create items and use them. E.g. your current code will look like:
var item = CreateItem(userItem);
item.UseItem(sender); // you don't care about specific type of item
Note: EF can use discriminator column to create entities of appropriate type automatically.
Just register building actions one time:
var builder = new ItemBuilder()
.RegisterBuilder(ItemType.Food, () => new Food())
.RegisterBuilder(ItemType.Weapon, () => new Weapon());
and use it later like this:
var item1 = builder.Build(ItemType.Food);
item1.UseItem(sender)
and here a builder code:
public class ItemBuilder
{
public ItemBase Build(ItemType itemType)
{
Func<ItemBase> buildAction;
if (itemBuilders.TryGetValue(itemType, out buildAction))
{
return buildAction();
}
return null;
}
public ItemBuilder RegisterBuilder(ItemType itemType, Func<ItemBase> buildAction)
{
itemBuilders.Add(itemType, buildAction);
return this;
}
private Dictionary<ItemType, Func<ItemBase>> itemBuilders = new Dictionary<ItemType, Func<ItemBase>> ();
}
Another option use a DI container like unity or somth:
UnityContainer.RegisterType<IItemBase, Food>("ItemType.Food");
UnityContainer.RegisterType<IItemBase, Weapon>("ItemType.Weapon");
and resolve
var item1 = UnityContainer.Resolve<IItemBase>(ItemType.Food.ToString());

How to create the base class of the generic type dictionary to store it in a list?

I have a lot of methods which return lists of data sets which are represented as the key (DateTime) and value (some ValueType or string, I can't know it) pair, and this data sets may have size for about 10 000 items and more. For example:
public List<DataSet> GetAnyData(...)
{
var resultList = new List<DataSet>(); // Create the list for the base class
resultList.Add(new DoubleDataSet()); // This classes will be filled in this method
resultList.Add(new StringDataSet()); // See the description for them below
return resultList;
}
Example of the result list from this method:
ListItem1 ListItem2
Date1 Values1 Date2 Values2
01:00 10.5 02:00 "a"
01:15 20.42 02:15 "b"
01:30 30.01 02:30 "c"
After receiving this data sets I need to process them by different ways (using LINQ). And my problem is: how to create the DataSet class?
I tryed a lot of solutions:
1) To Inherit the DataSet class from Dictionary using generic types:
abstract class DataSet<T> : Dictionary<DateTime, T> {...}
and to create specific classes:
class DoubleDataSet : DataSet<double> {...}
class StringDataSet : DataSet<string> {...}
But the problem is: how to create the base class which I could store in the List of data sets?
2) Create the class of the DataItem of the DataSet and use it in the first solution:
class DataItem
{
public double DoubleValue { get; set; }
public string StringValue { get; set; }
// etc
}
class DataSet : Dictionary<DateTime, DataItem> {...}
But the problem is: how to know what property of the DataItem I need to take?
3) Store data in the object class:
public class DataSet : Dictionary<DateTime, object> {...}
public class DoubleDataSet : DataSet
{
public new double this[DateTime key]
{
get { return base[key] as double; }
set { base[key] = value; }
}
}
But the problem is: what about boxing and unboxing? If I will have 50 000 values and every value will be converted to object and back to the required type it will spent a lot of time. Also using LINQ the type of DataSet value will be an object and I will need to know what type is it.
4) Store the dictionary with the data inside the DataSet class:
public class DataSet
{
public Dictionary<DateTime, DataItem> Items {get; set;}
}
public class DoubleDataSet : DataSet
{
public new Dictionary<DateTime, double> Items
{
get { return base.Items // It wouldn't work because there a new dictionary is created
.ToDictionary(q => q.Key, q => q.Value.DoubleValue); }
set { base.Items = value
.ToDictionary(q => q.Key, q => new DataItem(q.Value, null)); }
}
}
But the problem is: get { return base.Items.ToDictionary(q => q.Key, q => q.Value.DoubleValue); } returns a new instance of the Items dictionary and I can't modify the original list.
So help me please how to create the base class of the generic type dictionary to store it in a list?
I will be very grateful for any help! I know that there is an error of designing (or coding) and I will be glad if anyone will help me to find it! Is it even possible to solve this problem?
If I understood you correctly, you need to store and process objects of different types in your list, but at the processing time, you don't know on which type of object you are working on.
One thing you could try: Since your DataItem class knows what type it is, why don't you let it decide how it gets processed by it implementing the visitor pattern?
To implement this, you would have to do the following:
First, implement your different data items:
// base class you can use in dictionaries
// note that is does not store any data type
class DataItem
{
// method called from outside
// for processing the item
public abstract void GetProcessed(Processor p);
}
// DataItem which stores a double
class DoubleDataItem : DataItem
{
public double Data{get;set;}
public override void GetProcessed(Processor p)
{
p.Process(this);
}
}
// DataItem which stores a string
class StringDataItem : DataItem
{
public string Data {get;set}
public override void GetProcessed(Processor p)
{
p.Process(this);
}
}
Now the information, which type of data is used is only stored in the relevant class. Every class can also retrieve a processor (or a visitor). This processor/visitor can then have different implementations depending on the type it is operating on:
public class Processor
{
// do processing for double items
public void Process(DoubleDataItem d);
// do processing for string items
public void Process(StringDataItem d);
}
Now when you create your dictionary, the processor will execute the correct code depending on which item it is operating on:
var dictionary = new Dictionary<DateTime,DataItem>();
// fill dictionary with data item
var processor = new DoSomethingProcessor();
foreach (var entry in dictionary)
entry.Value.GetProcessed(processor);

Using reflection in C# to modify fields in a loop, extension method

After retrieving data from a database I find myself doing this to create a domain object from the data in a DataRow (in this case, a DVD):
DataRow drDvd = myDataTable.Rows[0];
Dvd myDvd = new Dvd();
myDvd.id = drDvd.Field<long>("id");
myDvd.title = drDvd.Field<string>("title");
myDvd.description = drDvd.Field<string>("description");
myDvd.releaseDate = drDvd.Field<DateTime>("releaseDate");
As I soon felt of course, I am doing this over and over in pseudo-code:
myDvd.field = drDvd.Field<field.type>(field.name);
And I wondered if I could get it into a loop, however I've never used reflection before. The code I tried is this:
Dvd aDvd = new Dvd();
Type t = aDvd.GetType();
FieldInfo[] fields = t.GetFields();
foreach (FieldInfo fi in fields)
{
fi.SetValue(aDvd, drDvd.Field<fi.FieldType>(fi.Name));
}
The problem is, as you may know, that the extension for the Field method of class DataRow does not accept a variable and needs to be explicitely filled in.
I am not that experienced in C# so I would like to pose the following two questions:
Is it good practice what I am trying to do?
How can I fill in the correct extension for Field<extension>(name)?
You'll need to get the method info for the generic method, and call invoke on it. This way you can pass in the generic type to it programmatically. I'm on my phone, but it should look something like this:
MethodInfo mField = typeof(Dvd).GetMethod("Field");
MethodInfo genericMethod = mField.MakeGenericMethod(new Type[] { fi.FieldType });
GenericMethod.Invoke(aDvd,new Object[]{fi.Name});
It is usually a bad practice to use reflection when it is not really necessary. Because reflection methods are checked at runtime rather than compile time, faulty code is harder to track, because the compiler can't check for errors.
If I were you, id have a look at the Entity Framework, because youre basically mapping database data to domain objects. http://msdn.microsoft.com/en-us/library/aa697427%28v=vs.80%29.aspx
This is one of the way of constructing and populating your domain object
DataRow drDvd = new DataRow();
Dvd aDvd = new Dvd();
Type type = typeof(Dvd);
foreach (FieldInfo fi in type.GetFields())
{
fi.SetValue(aDvd, drDvd[fi.Name]);
}
Your approach of using DataRow.Field may be round about. In you case, it is not applicable.
Alternatively you can think about using one of the Entity frameworks (NHibernate, Microsoft EF etc) in your application.
I would do a custom attribute. In doing an attribute you are stuck with your field name being the same as the database. I currently use this in my current applications and it works great. It is very similar to Entity SQL.
public class SqlMetaAttribute : Attribute
{
public string ColumnName { get; set; }
}
Then you have your class like this
public class Person
{
[SqlMeta(ColumnName = "First_Name")]
publice string FirstName { get; set; }
[SqlMeta(ColumnName = "Last_Name")]
publice string LastName { get; set; }
}
You would then have a helper class with the same kind of functions. In this case I am assuming the outside caller is looping through the datatable. Making it generic using the template T makes this really reusable. Rather than just having a "DVD" type implementation and coping and pasting for another.
public static T CreateObjectFromRow<T>(DataRow row)
{
var newObject = new T();
if (row != null) SetAllProperties(row, newObject);
return newObject;
}
public static void SetAllProperties<T>(DataRow row, T newObject)
{
var properties = typeof(T).GetProperties();
foreach(var propertyInfo in properties)
{
SetPropertyValue(row, newObject, propertyInfo);
}
}
public static void SetPropertyValue(DataRow row, T newObject, PropertyInfo propertyInfo)
{
var columnAttribute = propertyInfo.FindAttribute<SqlMetaAttribute>();
if (columnAttribute == null) return;
// If the row type is different than the object type and exception will be thrown, but that is
// okay because if that happens you have to fix your object you are using, or might need some
// more custom code to help you with that.
propertyInfo.SetValue(newObject, row.GetValue<object>(columnAttribute.ColumnName), null);
}
// Extension method for row.GetValue<object> used above
public static T GetValue<T>(this DataRow row, string columnName)
{
if (row.ColumnNameNotFound(columnName) || row.Table.Columns[columnName] == null || row[columnName] is DBNull)
{
return default(T);
}
return (T)row[columnName];
}

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();

define and initialize a list containing a struct with a func<> member

I have got a List<Problem> which I want to export to a column based file format (excel in my case).
So for each data member of my Problem class there is a corresponding column in my file.
Each column has got a header (string) and a function which will get that the row-value from a class Problem object (which can be of type string, double or DateTime).
To define the relation from Problem object to column I use a class like this:
private class ExportColumn<T>
{
public ExportColumn (string colHeader, Func<Probleme, T> colGetter)
{
header = colHeader;
getValue = colGetter;
}
public string header;
// This function will return the row value for that column for one problem
public Func<Probleme, T> getValue;
}
How can I define a list of many ExportColumns, to iterate over and extract all the row data for all problems?
Something like this (pseudocode):
private static string GetThema(Probleme problem)
{
return problem.Thema;
}
private static double GetStatus(Probleme problem)
{
return problem.Status + 100;
}
private static readonly List<ExportColumn> colList = new List<ExportColumn>
{
("Thema", GetThema),
("Status", GetStatus)
...
};
foreach(ExportColumn col in colList)
{
foreach(Problem problem in Probleme)
{
WriteToFile(col.header, col.getValue(problem))
}
}
I'm pretty new to C#, so perhaps this is the wrong way to solve that issue. Basically I just want one place in my code, where I define all headers, the corresponding function which will extract the value from a problem in a typesafe way.
Thanks for any help!
EDIT: In C++ words: I would like a vector of class ExportColumn which contain a string and a function pointer to a getter method with that signature : T getValue(const Problem &problem)
You can't do that, if the supplied generic parameter changes. You will need to create the struct without generics:
class ExportColumn
{
public ExportColumn (string colHeader, Func<Probleme, object> colGetter)
{
Header = colHeader;
GetValue = colGetter;
}
public string Header {get; private set;}
// This function will return the row value for that column for one problem
public Func<Probleme, object> GetValue {get; private set;}
}
Instances of this struct can now be put into a List<ExportColumn>.
You could initialize that list like so:
List<ExportColumn> colList = new List<ExportColumn>
{
new ExportColumn("Thema", p => p.Thema),
new ExportColumn("Status", p => p.Status + 100)
};

Categories