How can I refactor this C# code - c#
Basically I have a method that takes an object and sets another objects properties based on the object passed in.
e.g:
private void SetObject(MyClass object)
{
MyClass2 object2 = new MyClass2();
object2.Property1 = HelperClass.Convert(object.Property1);
//....
// Lots more code ....
//....
}
Now the method is 53 lines long because there are alot of properties to set. The method seems too long to me but I'm struggling to work out how I can possibly break it down.
One option is to try and group up similar properties and pass the object around as a reference to different methods that set these similar properties, but that doesn't seem to sit right with me.
Or I could create a constructor for MyClass2 that accepts a MyClass1 but that doesn't seem right either.
Anyway would welcome some suggestions.
EDIT: Ok thanks for the replies I'll have to give more info, the property names arent the same and I have to call some conversion methods as well. Reflection wouldn't be good because of this and also the performance hit. Automapper I think for the same reasons.
A real example of the code:
private ReportType GetReportFromItem(SPWeb web, SPListItem item)
{
ReportType reportType = new ReportType();
reportType.ReportID = int.Parse(item["Report ID"].ToString());
reportType.Name = item["Title"].ToString();
reportType.SourceLocation = FieldHelpers.GetUri(item["Source Location"]);
reportType.TargetLocation = FieldHelpers.GetUri(item["Document Library"]);
SPFieldUserValue group1 =
new SPFieldUserValue(web, FieldHelpers.GetStringFieldValue(item, "Security Group 1"));
reportType.SecurityGroup1 = group1.LookupValue;
SPFieldUserValue group2 =
new SPFieldUserValue(web, FieldHelpers.GetStringFieldValue(item, "Security Group 2"));
reportType.SecurityGroup2 = group2.LookupValue;
SPFieldUserValue group3 =
new SPFieldUserValue(web, FieldHelpers.GetStringFieldValue(item, "Security Group 3"));
reportType.SecurityGroup3 = group3.LookupValue;
SPFieldUserValue group4 =
new SPFieldUserValue(web, FieldHelpers.GetStringFieldValue(item, "Security Group 4"));
// More code
//...
//...
}
Sounds like a job for AutoMapper
use reflection to do it. probably have a method like this:
private void SetProperties<T>(List<T> objects, List<Tuple<string, object>> propsAndValues) where T:<your_class>
{
Type type = typeof(T);
var propInfos = propsAndValues.ToDictionary(key => type.GetProperty(key.Item1, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.SetProperty), elem => elem.Item2);
objects.AsParallel().ForAll(obj =>
{
obj.SetProps(propInfos);
});
}
public static void SetProps<T>(this T obj, Dictionary<PropertyInfo, object> propInfos) where T : <your_class>
{
foreach (var propInfo in propInfos)
{
propInfo.Key.SetValue(obj, propInfo.Value, null);
}
}
There are a few strategies that came to my mind for doing this, all with their own advantages and disadvantages. Also, I was not familiar with it, but the AutoMapper tool linked to in a different answer to your question sounds like it also could be a good solution. (Also, if there were any way of deriving your classes all from the same class, or storing the properties themselves in a struct instead of directly in the classes, those seem like things to consider as well.)
Reflection
This was mentioned in this answer. However, I am not sure I totally understand the intended use of the functions in that answer, since I don't see a GetValue call, nor any mapping between two types. Also, I can see times where you might want to create something to allow for two different names to map to one another, or for conversion between two types. For a rather generic solution, I would probably go with an approach like this:
Create an extension class with one or more methods that will use reflection to copy based on identical property names and/or pre-defined configuration objects.
For each pair of types that has properties that won't have identical names, create a configuration object mapping the names to each other.
For properties that you won't want to be copied, create a configuration object that holds a list of names to ignore.
I don't actually see anything objectionable about passing one class to another in the constructor if the intent is to copy the properties, seems more of a matter of style than anything hard and fast.
Example Code
classes to be copied to:
public class MyClass2
{
public int Property1 { get; set; }
public int Property2 { get; set; }
public string Property3WithOtherName { get; set; }
public double Property4 { get; set; }
public string Property5WithDifferentName { get; set; }
public string TestIntToString { get; set; }
public int TestStringToInt { get; set; }
}
public class MyClass3
{
public int Prop1 { get; set; }
public int Prop2 { get; set; }
public string Prop3OtherName { get; set; }
public double Prop4 { get; set; }
public string Prop5DiffName { get; set; }
public string PropOnlyClass3 { get; set; }
public string[] StringArray { get; set; }
}
class to be copied, w/ mapping info to the other objects:
public class MyClass
{
public int Property1 { get; set; }
public int Property2 { get; set; }
public string Property3 { get; set; }
public double Property4 { get; set; }
public string Property5 { get; set; }
public double PropertyDontCopy { get; set; }
public string PropertyOnlyClass3 { get; set; }
public int[] PropertyIgnoreMe { get; set; }
public string[] StringArray { get; set; }
public int TestIntToString { get; set; }
public string TestStringToInt { get; set; }
# region Static Property Mapping Information
// this is one possibility for creating and storing the mapping
// information: the class uses two dictionaries, one that links
// the other type with a dictionary of mapped properties, and
// one that links the other type with a list of excluded ones.
public static Dictionary<Type, Dictionary<string, string>>
PropertyMappings =
new Dictionary<Type, Dictionary<string, string>>
{
{
typeof(MyClass2),
new Dictionary<string, string>
{
{ "Property3", "Property3WithOtherName" },
{ "Property5", "Property5WithDifferentName" },
}
},
{
typeof(MyClass3),
new Dictionary<string, string>
{
{ "Property1", "Prop1" },
{ "Property2", "Prop2" },
{ "Property3", "Prop3OtherName" },
{ "Property4", "Prop4" },
{ "Property5", "Prop5DiffName" },
{ "PropertyOnlyClass3", "PropOnlyClass3" },
}
},
};
public static Dictionary<Type, List<string>>
UnmappedProperties =
new Dictionary<Type, List<string>>
{
{
typeof(MyClass2),
new List<string>
{
"PropertyDontCopy",
"PropertyOnlyClass3",
"PropertyIgnoreMe"
}
},
{
typeof(MyClass3),
new List<string>
{
"PropertyDontCopy",
"PropertyIgnoreMe"
}
}
};
// this function pulls together an individual property mapping
public static Tuple<Dictionary<string, string>, List<string>>
MapInfo<TOtherType>()
{
return
new Tuple<Dictionary<string,string>,List<string>>
(
PropertyMappings[typeof(TOtherType)],
UnmappedProperties[typeof(TOtherType)]
);
}
#endregion
}
Mapping Extension Class:
public static class MappingExtensions
{
// this is one possibility for setting up object mappings
#region Type Map Definition Section
// * set up the MapInfo<TOther>() call in each object to map
// * setup as follows to map two types to an actual map
public static Tuple<Type, Type> MapFromTo<TFromType, TToType>()
{
return Tuple.Create<Type,Type>(typeof(TFromType), typeof(TToType));
}
static Dictionary<
Tuple<Type, Type>,
Tuple<Dictionary<string, string>, List<string>>
>
MappingDefinitions =
new Dictionary <
Tuple<Type,Type>,
Tuple<Dictionary<string,string>,List<string>>
>
{
{ MapFromTo<MyClass,MyClass2>(), MyClass.MapInfo<MyClass2>() },
{ MapFromTo<MyClass,MyClass3>(), MyClass.MapInfo<MyClass3>() },
};
#endregion
// method using Reflection.GetPropertyInfo and mapping info to do copying
// * for fields you will need to reflect using GetFieldInfo() instead
// * for both you will need to reflect using GetMemberInfo() instead
public static void CopyFrom<TFromType, TToType>(
this TToType parThis,
TFromType parObjectToCopy
)
{
var Map = MappingDefinitions[MapFromTo<TFromType, TToType>()];
Dictionary<string,string> MappedNames = Map.Item1;
List<string> ExcludedNames = Map.Item2;
Type FromType = typeof(TFromType); Type ToType = typeof(TToType);
// ------------------------------------------------------------------------
// Step 1: Collect PIs for TToType and TFromType for Copying
// ------------------------------------------------------------------------
// Get PropertyInfos for TToType
// the desired property types to reflect for ToType
var ToBindings =
BindingFlags.Public | BindingFlags.NonPublic // property visibility
| BindingFlags.Instance // instance properties
| BindingFlags.SetProperty; // sets for ToType
// reflect an array of all properties for this type
var ToPIs = ToType.GetProperties(ToBindings);
// checks for mapped properties or exclusions not defined for the class
#if DEBUG
var MapErrors =
from name in
MappedNames.Values
where !ToPIs.Any(pi => pi.Name == name)
select string.Format(
"CopyFrom<{0},{1}>: mapped property '{2}' not defined for {1}",
FromType.Name, ToType.Name, name
);
#endif
// ------------------------------------------------------------------------
// Get PropertyInfos for TFromType
// the desired property types to reflect; if you want to use fields, too,
// you can do GetMemberInfo instead of GetPropertyInfo below
var FromBindings =
BindingFlags.Public | BindingFlags.NonPublic // property visibility
| BindingFlags.Instance // instance/static
| BindingFlags.GetProperty; // gets for FromType
// reflect all properties from the FromType
var FromPIs = FromType.GetProperties(FromBindings);
// checks for mapped properties or exclusions not defined for the class
#if DEBUG
MapErrors = MapErrors.Concat(
from mn in MappedNames.Keys.Concat(
ExcludedNames)
where !FromPIs.Any(pi => pi.Name == mn)
select string.Format(
"CopyFrom<{0},{1}>: mapped property '{2}' not defined for {1}",
FromType.Name, ToType.Name, mn
)
);
// if there were any errors, aggregate and throw
if (MapErrors.Count() > 0)
throw new Exception(
MapErrors.Aggregate(
"", (a,b)=>string.Format("{0}{1}{2}",a,Environment.NewLine,b)
));
#endif
// exclude anything in the exclusions or not in the ToPIs
FromPIs = FromPIs.Where(
fromPI =>
!ExcludedNames.Contains(fromPI.Name)
&& ToPIs.Select(toPI => toPI.Name).Concat(MappedNames.Keys)
.Contains(fromPI.Name)
)
.ToArray();
// Step 1 Complete
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
// Step 2: Copy Property Values from Source to Destination
#if DEBUG
Console.WriteLine("Copying " + FromType.Name + " to " + ToType.Name);
#endif
// we're using FromPIs to drive the loop because we've already elimiated
// all items that don't have a matching value in ToPIs
foreach (PropertyInfo FromPI in FromPIs)
{
PropertyInfo ToPI;
// if the 'from' property name exists in the mapping, use the mapped
// name to find ToPI, otherwise use ToPI matching the 'from' name
if (MappedNames.Keys.Contains(FromPI.Name))
ToPI = ToPIs.First(pi => pi.Name == MappedNames[FromPI.Name]);
else
ToPI = ToPIs.First(pi => pi.Name == FromPI.Name);
Type FromPropertyType = FromPI.PropertyType;
Type ToPropertyType = ToPI.PropertyType;
// retrieve the property value from the object we're copying from; keep
// in mind if this copies by-reference for arrays and other ref types,
// so you will need to deal with it if you want other behavior
object PropertyValue = FromPI.GetValue(parObjectToCopy, null);
// only need this if there are properties with incompatible types
// * implement IConvertible for user-defined types to allow conversion
// * you can try/catch if you want to ignore items which don't convert
if (!ToPropertyType.IsAssignableFrom(FromPropertyType))
PropertyValue = Convert.ChangeType(PropertyValue, ToPropertyType);
// set the property value on the object we're copying to
ToPI.SetValue(parThis, PropertyValue, null);
#if DEBUG
Console.WriteLine(
"\t"
+ "(" + ToPI.PropertyType.Name + ")" + ToPI.Name
+ " := "
+ "(" + FromPI.PropertyType.Name + ")" + FromPI.Name
+ " == "
+ ((ToPI.PropertyType.Name == "String") ? "'" : "")
+ PropertyValue.ToString()
+ ((ToPI.PropertyType.Name == "String") ? "'" : "")
);
#endif
}
// Step 2 Complete
// ------------------------------------------------------------------------
}
}
Test Method:
public void RunTest()
{
MyClass Test1 = new MyClass();
Test1.Property1 = 1;
Test1.Property2 = 2;
Test1.Property3 = "Property3String";
Test1.Property4 = 4.0;
Test1.Property5 = "Property5String";
Test1.PropertyDontCopy = 100.0;
Test1.PropertyIgnoreMe = new int[] { 0, 1, 2, 3 };
Test1.PropertyOnlyClass3 = "Class3OnlyString";
Test1.StringArray = new string[] { "String0", "String1", "String2" };
Test1.TestIntToString = 123456;
Test1.TestStringToInt = "654321";
Console.WriteLine("-------------------------------------");
Console.WriteLine("Copying: Test1 to Test2");
Console.WriteLine("-------------------------------------");
MyClass2 Test2 = new MyClass2();
Test2.CopyFrom(Test1);
Console.WriteLine("-------------------------------------");
Console.WriteLine("Copying: Test1 to Test3");
Console.WriteLine("-------------------------------------");
MyClass3 Test3 = new MyClass3();
Test3.CopyFrom(Test1);
Console.WriteLine("-------------------------------------");
Console.WriteLine("Done");
Console.WriteLine("-------------------------------------");
}
}
Output:
-------------------------------------
Copying: Test1 to Test2
-------------------------------------
Copying MyClass to MyClass2
(Int32)Property1 := (Int32)Property1 == 1
(Int32)Property2 := (Int32)Property2 == 2
(String)Property3WithOtherName := (String)Property3 == 'Property3String'
(Double)Property4 := (Double)Property4 == 4
(String)Property5WithDifferentName := (String)Property5 == 'Property5String'
(String)TestIntToString := (Int32)TestIntToString == '123456'
(Int32)TestStringToInt := (String)TestStringToInt == 654321
-------------------------------------
Copying: Test1 to Test3
-------------------------------------
Copying MyClass to MyClass3
(Int32)Prop1 := (Int32)Property1 == 1
(Int32)Prop2 := (Int32)Property2 == 2
(String)Prop3OtherName := (String)Property3 == 'Property3String'
(Double)Prop4 := (Double)Property4 == 4
(String)Prop5DiffName := (String)Property5 == 'Property5String'
(String)PropOnlyClass3 := (String)PropertyOnlyClass3 == 'Class3OnlyString'
(String[])StringArray := (String[])StringArray == System.String[]
-------------------------------------
Done
-------------------------------------
NOTE: if you have use for copying back the other direction, or would like, for instance, to create your mappings with a code generator, you may want to go with having your mappings in a separate static variable somewhere else, mapped by both types, rather than storing your mappings directly within a class. If you want to reverse direction, you could probably just add a flag to the existing code that lets you invert the meanings of the tables and of the passed types, although I would recommend changing references to TToType and TFromType to TType1 and TType2.
For having a code generator generate the mapping it's probably a good bit easier to separate it into the separate class with two type parameters for the generic, so that you don't necessarily have to worry about putting those definitions directly within your classes; I was torn about how to do it when I wrote the code, but I do think that would probably have been a more flexible option. (On the other hand, it probably means needing larger overall structure, which is why I broke it out like I did in the first place.) Another advantage to this way is that you don't necessarily need to create everything all at once, you could conceivable change to member variables that let you create them on-the-fly as needed, possibly through a function param or through an additional interface on your object.
Code Generation
This can be used by itself, it can also be used as a tool in conjunction with either of the next two methods. But the idea is that you can create a CSV or use some other method to hold your mapping data, and then you can create class templates (in separate files or in code) with placeholders in them (e.g., ${PROPERTIES_LIST}) that you can use to do .Replace() operations on, or you can create templates of the .xsd files used for typed dataset generation in the next section, generate the individual lines of the .xsd from the list, or, finally you could create the structures that hold the mapping information for something like the solution I give in the reflection section.
One thing I've done in the past which was very handy was to just whip up a typed dataset with a table structure that can hold all the information I need to do my code generation, and use the old .NET 2.0 version of the GridView to allow me to enter the data in. But even with just a simple XmlDocument or CSV file(s), it should be very easy to enter your properties mappings and read them back in, and the none of the generation for any of the scenarios here is likely to be terribly much effort, usually even compared to having to type it all in by hand just once or twice, plus if it's something that gets updated at all often, you will eventually save yourself hand-coding errors and debug time as well.
Typed Data Sets
Although it's sort of beyond the intended use of the data set, if you don't need super-high performance, it can come in handy. After defining a table in the dataset and building, you have a set of strongly-typed objects that can be used, for instance, to hold objects representing the tables and columns or rows. So, you could easily create a typed dataset from scratch using column names and types matching your objects, and then loop through the column names to get the names of your properties. While not as powerful as reflection, it could be a very quick way of getting yourself set up to copy two objects with the same property names. Of course, it's a much more limited usefulness because it's only going to be useful if you have control over your types, and if you don't have any particularly complicated needs for copying the data.
But it also can let you do things like:
MyClassDataRow Object1 = MyDataSet.MyClassTable.NewRow();
Object1.Prop1 = 123;
Object2.Prop2 = "Hello Dataset";
// etc...
MyClass2DataRow Object2 = MyDataSet.MyClass2Table.NewRow();
foreach (DataColumn ColumnName in MyClassTable.Columns)
Object2[ColumnName] = Object1[ColumnName];
with little additional effort. And, if you're in a situation where you want to be able save your objects out to disk, just add the rows to the table you've got a great way of serializing the data with the DataSet.ReadXml and DataSet.WriteXml calls.
Also, for the reflection solution, as long as your requirements weren't too complicated, you could create your mapping definitions by creating a typed dataset with a couple of tables with column names matching those to map, and then using the information in the DataColumns of the tables to do your mapping definition instead of Dictionaries, Lists, and Tuples. Using some sort of predefined names prefixes or otherwise unused types in your table, you ought to be able to duplicate most of the functionality in the sample code.
Related
C# - Generic custom List.Add method to add checks before adding?
I am wondering if something like this is possible. I am looking to create a method that can be called instead of List.Add. The method would check any/all string properties and make sure they don't exceed their specific given max lengths, and if so truncate to proper size. I would ideally like it to be generic so that it will not only work for ObjectA, but also ObjectB, ObjectC, etc. I am open to any and all suggestions. I know it seems like a weird thing to do, but I have a lot of different objects I am working with and potentially millions of those object instances in totality across all my lists. I mainly just need a way to ensure that any objects with properties exceeding their max string limit are truncated and logged via the Worker class in a timely way. Thanks! public class ObjectA { public Guid aID {get; set;} [MaxLength(128)] public string aName {get; set;} [MaxLegnth(30)] public string aType {get; set;} } -- public class Worker { private void Work() { List<ObjectA> listOfA = new List<ObjectA>(); listOfA.CustomAddMethod(new ObjectA(new Guid, "Something", "Unknown")); } // ?????? private CustomAddMethod(T object) { foreach property { if (isStringProperty && isGreaterThanMaxLength) { // truncate to proper size // log for truncation message } // Then add to list } } }
You can create an extension method. Here is a code snip. You can improve the performance by implementing a cache, for example, using a dictionary to store the properties and the MaxLengthAttribute based on object's type. public static class ListExtensions { public static void CustomAdd<T>(this List<T> list, T item, Action<string> logger = null) { var propertyInfos = typeof(T) .GetProperties(BindingFlags.Instance | BindingFlags.Public) .Where(propertyInfo => propertyInfo.PropertyType == typeof(string)) .ToList(); foreach (var propInfo in propertyInfos) { var maxLengthAttr = propInfo .GetCustomAttributes(typeof(MaxLengthAttribute)) .Cast<MaxLengthAttribute>() .FirstOrDefault(); if (maxLengthAttr is null) continue; var currentString = (string)propInfo.GetValue(item); if (!maxLengthAttr.IsValid(currentString)) { var newValue = currentString.Substring(0, maxLengthAttr.Length); logger?.Invoke( $"Resolving error: {maxLengthAttr.FormatErrorMessage(propInfo.Name)}\n" + $"Old Value: {currentString}\n" + $"New Value: {newValue}" ); propInfo.SetValue(item, newValue); } } list.Add(item); } } Example of usage (code removed for brevity): public class Person { [MaxLength(4)] public string Name { get; set; } } ... var personList = new List<Person>(); personList.CustomAdd( new Person {Name = "John Doe"}, message => Debug.WriteLine(message) ); ... As result the Jhon Doe string will be trimmed to Jhon
Reflection - SetValue from deep context
I am facing an issue, surely due to my lack of knowledge in the reflection process, while trying to set a "complex" class hierarchy based on Json files. Here are my main model : public class Names { public Weapons Weapons { get; set; } public Armors Armors { get; set; } public Utilities Utilities { get; set; } public Names() { Weapons = new Weapons(); Armors = new Armors(); Utilities = new Utilities(); } } Each of them having a list of sub-model like this: public class Weapons { public BattleAxe BattleAxe { get; set; } = new BattleAxe(); public Bomb_Missile Bomb_Missile { get; set; } = new Bomb_Missile(); // etc... Around 20 to 25 } And finally the ended model which is the exact equivalent of each json files but may have very different properties : public class BattleAxe { public string[] Normal { get; set; } = new string[0]; public string[] DescriptiveAdjective { get; set; } = new string[0]; public string[] Material { get; set; } = new string[0]; public string[] Type { get; set; } = new string[0]; public string[] Title { get; set; } = new string[0]; public string[] Of { get; set; } = new string[0]; public string[] NormalForTitle { get; set; } = new string[0]; } Since the MS Json deserializer does not support the conversion to a $type as Newtonsoft before, I tried to populate the values using reflection too like this (I've removed all the null-check for code readability) : public static void Load() { Names = new Names(); foreach (var category in Names.GetType().GetProperties()) { if (category is not null && !(category.GetGetMethod()?.IsStatic ?? false)) { var categoryType = category.PropertyType; foreach (var item in category.PropertyType.GetProperties()) { var itemType = item.PropertyType; var subTypeData = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(File.ReadAllText($"./Assets/Names/{categoryType.Name}/{itemType.Name}.json")); var concreteObj = Activator.CreateInstance(itemType); foreach (var key in subTypeData.Keys) { if (itemType.GetProperty(key) is not null && concreteObj is not null) { var prop = concreteObj.GetType().GetProperty(key); var convertedValue = ConvertJsonType(subTypeData[key], subTypeData[key].ValueKind, out var isReferenceType); // It fails here prop.SetValue( isReferenceType ? convertedValue : null, !isReferenceType ? convertedValue : null ); } } item.SetValue(concreteObj, null); } } } } So it fails at the prop.SetValue(...) of the deepest object in the hierarchy with a different error depending on the type of value to set. If it is a reference, it throws a System.Reflection.TargetException : 'Object does not match target type' Exception And if it is value, it throw a System.Reflection.TargetException : 'Non-static method requires a target.' Knowing that I do not have problems around the deserialization as shown here, only the fact that I use a dynamic type (and my instinct tells me it is actually the problem...) I do not add the ConvertJsonType(...) body as it is functional and really simple I am more interested in the 'why' than the 'how' so if you can explain me the 'theory' behind the problem, that would help quite a lot :) Thank you! PS: I know I can simplify the things in a more readable/performant way but I must achieve it with reflection for personal learning :) Same for the System.Text.Json namespace, I do not intend to switch back to Newtonsoft for that
When calling SetValue(instance, value) you should pass the object which property should be set. It's a wild guess, but you could try this: prop.SetValue(concreteObj, !isReferenceType ? convertedValue : null); Because you want to fill the properties of concreteObj, not the value it self. If you look at the object prop it was a return value of concreteObj.GetType().GetProperty(key);. If you look at it close, The GetProperty is a method from Type which isn't bound to any instance. So that's why you need to pass the instance of the object as the first parameter. I mean this in a positive way: The itemType.GetProperty(key) is called every iteration, it will be the same value each iteration, you could bring it before the loop.
As docs state TargetException is thrown when: The type of obj does not match the target type, or a property is an instance property but obj is null. Passing null for obj in SetValue is valid when you are trying to set value for static property, not an instance one. Property type being a reference one has nothing to do with property being instance or static one so your call should look something like: prop.SetValue(concreteObj, convertedValue); Also your item.SetValue(concreteObj, null); does not look right cause concreteObj should be second argument in this call. Something like this: item.SetValue(Names, concreteObj); Also if you want only instance properties you can provide BindingFlags to get only instance properties: foreach (var category in Names.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)) Also I would say that category is not null check is redundant so in pair with providing BindingFlags you should remove the if completely.
Is it possible to get value of a method thru Property
I have to export data to Excel programmatically. I have a class with several properties. I was wondering if it's possible to retrieve values of all properties using a loop. For instance: public class SqueezeProperties { public int WidthField { get; set; } public string Width_unit { get; set; } public int ResPressure { get; set; } public int DensityField { get; set; } ...... } While writing to excel, I code as: t = typeof(SqueezeProperties); foreach (PropertyInfo field in t.GetProperties(BindingFlags.Public | BindingFlags.Instance)) oSheet.Cells[r, c++] = field.Name; Now to input values, is there any way that I can iterate and find values of all properties that can be accessed, and store them in excel? I doubt if it is even possible. I just taught myself how to access the property name & its details, so I thought maybe the other thing could also be possible and I am simply unaware of it. Thanks
You can use PropertyInfo.GetValue. (However according to specification the order of your properties is not guaranteed to be the same as the definition order. So to be safe you might want to order them.) Also instead of getting them via reflection you could create a collection manually instead, this would take care of the order already. e.g. var properties = new Expression<Func<SqueezeProperties, object>>[] { o => o.WidthField, o => o.Width_unit, //... }; foreach (var exp in properties) { var mem = (MemberExpression)exp.Body; var prop = (PropertyInfo)mem.Member; oSheet.Cells[r, c++] = prop.GetValue(squeezePropertiesInstance, null); }
Serializing with ProtoBuf.NET without tagging members
I've read somewhere a comment by the author of ProtoBuf.NET that: There are options to automatically infer the numbers, but that is brittle and not recommended. Only use this if you know you never need to add more members (it orders them alphabetically, so adding a new AardvarkCount will break everything). This is exactly that sort of situation I am interested in :) I have something that is akin to a map-reduce scenario where I want to serialize results generated on remote machines using protocol buffers (e.g. the "map" side of map-reduce) and later read them and combine those results for further processing (e.g. the "reduce" side). I don't want to start an attribute decoration marathon over every possible class I have that might get serialized during this process, and I do find the protocol buffers to be very alluring as I can create result with Mono and consume them effortlessly on MS.NET and vice-versa... The apparent downsides of not pre-tagging the members doesn't bother me as exactly the same software revision does generation/consumptionn, so I don't need do worry about new members popping up in the code and messing my whole scheme... So in short, my question is: How do I do it (Serialize with ProtoBuf.NET without tagging/building Meta classes on my own)? Is there any hole in my scheme that I've glaringly missed?
If you can live with a single attribute, then the trick is: [ProtoContract(ImplicitFields = ImplicitFields.AllPublic)] public class WithImplicitFields { public int X { get; set; } public string Y { get; set; } } there are 2 options here; AllPublic works like XmlSerializer - public properties and fields are serialized (using the alphabetic order to choose tag numbers); AllFields works a bit like BinaryFormatter - the fields are serialized (again, alphabetic). I can't remember if this is yet available on the v2 API; I know it is on my list of things to ensure work! But if you want it in v2 without any attributes, I'm sure I can add an Add(ImplicitFields) overload. As long as the 2 ends are never out of step, this is fine. If you store the data, or don't version the two ends "in step", then there could be problems. See also the intellisense comments on the enum (which pretty much repeats the warning that you are already aware of).
I had the same problem and that how I've resolved it with TypeModel. It's based on properties ordered by their name (however it doesn't check on property setter/getter existence or serialize-ability of a given type): [TestFixture] public class InferredProtoPoc { [Test] public void UsageTest() { var model = TypeModel.Create(); // Dynamically create the model for MyPoco AddProperties(model, typeof(MyPoco)); // Display the Generated Schema of MyPoco Console.WriteLine(model.GetSchema(typeof(MyPoco))); var instance = new MyPoco { IntegerProperty = 42, StringProperty = "Foobar", Containers = new List<EmbeddedPoco> { new EmbeddedPoco { Id = 12, Name = "MyFirstOne" }, new EmbeddedPoco { Id = 13, Name = "EmbeddedAgain" } } }; var ms = new MemoryStream(); model.Serialize(ms, instance); ms.Seek(0, SeekOrigin.Begin); var res = (MyPoco) model.Deserialize(ms, null, typeof(MyPoco)); Assert.IsNotNull(res); Assert.AreEqual(42, res.IntegerProperty); Assert.AreEqual("Foobar", res.StringProperty); var list = res.Containers; Assert.IsNotNull(list); Assert.AreEqual(2, list.Count); Assert.IsTrue(list.Any(x => x.Id == 12)); Assert.IsTrue(list.Where(x => x.Id == 12).Any(x => x.Name == "MyFirstOne")); Assert.IsTrue(list.Any(x => x.Id == 13)); Assert.IsTrue(list.Where(x => x.Id == 13).Any(x => x.Name == "EmbeddedAgain")); } private static void AddProperties(RuntimeTypeModel model, Type type) { var metaType = model.Add(type, true); foreach (var property in type.GetProperties().OrderBy(x => x.Name)) { metaType.Add(property.Name); var propertyType = property.PropertyType; if (!IsBuiltinType(propertyType) && !model.IsDefined(propertyType) && propertyType.GetProperties().Length > 0) { AddProperties(model, propertyType); } } } private static bool IsBuiltinType(Type type) { return type.IsValueType || type == typeof (string); } } public class MyPoco { public int IntegerProperty { get; set; } public string StringProperty { get; set; } public List<EmbeddedPoco> Containers { get; set; } } public class EmbeddedPoco { public int Id { get; set; } public String Name { get; set; } } And that's what you get from running it. message EmbeddedPoco { optional int32 Id = 1; optional string Name = 2; } message MyPoco { repeated EmbeddedPoco Containers = 1; optional int32 IntegerProperty = 2; optional string StringProperty = 3; } For performance, you could opt to compile the TypeModel, and/or store the generated proto for future uses. Beware however that hidden dependency on Protocol Buffer could be dangerous in the long run if the poco (Plain old container object) evolves.
Architecturally speaking, how should I replace an extremely large switch statement with something more manageable?
EDIT 1: Forgot to add the nested property curve ball. UPDATE: I have chosen #mtazva's answer as that was the preferred solution for my specific case. In retrospect, I asked a general question with a very specific example and I believe that ended up confusing everyone (or maybe just me) as to what the question was exactly. I do believe the general question has been answered as well (see the Strategy pattern answers and links). Thanks everyone! Large switch statements obviously smell and I have seen some links on how you could do this with a dictionary that maps to functions. But I'm wondering if there is a better (or smarter way) to do this? In a way, this is a question I've always sort of had rolling around in the back of my head but never really had a good solution to. This question stemmed from another question I asked earlier: How to select all the values of an object's property on a list of typed objects in .Net with C# Here is an example class I'm working with (from an external source): public class NestedGameInfoObject { public string NestedName { get; set; } public int NestedIntValue { get; set; } public decimal NestedDecimalValue { get; set; } } public class GameInfo { public int UserId { get; set; } public int MatchesWon { get; set; } public long BulletsFired { get; set; } public string LastLevelVisited { get; set; } public NestedGameInfoObject SuperCoolNestedGameInfo { get; set; } // thousands more of these } Unfortunately, this is coming from an external source... imagine a HUGE data dump from Grand Theft Auto or something. And I want to get just a small cross section of a list of these objects. Imagine we want to be able to compare you with a bunch of your friends' game info objects. An individual result for one user would look like this: public class MyResult { public int UserId { get; set; } // user id from above object public string ResultValue { get; set; } // one of the value fields from above with .ToString() executed on it } And an example of what I want to replace with something more manageable (believe me, I DON'T want to be maintaining this monster switch statement): const int MATCHES_WON = 1; const int BULLETS_FIRED = 2; const int NESTED_INT = 3; public static List<MyResult> GetMyResult(GameInfo[] gameInfos, int input) { var output = new List<MyResult>(); switch(input) { case MATCHES_WON: output = gameInfos.Select(x => new MyResult() { UserId = x.UserId, ResultValue = x.MatchesWon.ToString() }).ToList<MyResult>(); break; case BULLETS_FIRED: output = gameInfos.Select(x => new MyResult() { UserId = x.UserId, ResultValue = x.BulletsFired.ToString() }).ToList<MyResult>(); break; case NESTED_INT: output = gameInfos.Select(x => new MyResult() { UserId = x.UserId, ResultValue = x.SuperCoolNestedGameInfo.NestedIntValue.ToString() }).ToList<MyResult>(); break; // ad nauseum } return output; } So the question is are there any reasonable ways to manage this beast? What I'd really like is a dynamic way to get this info in case that initial object changes (more game info properties are added, for instance). Is there a better way to architect this so it's less clumsy?
I think your first sentence eluded to what is probably the most reasonable solution: some form of dictionary mapping values to methods. For example, you could define a static Dictionary<int, func<GameInfo, string>>, where each value such as MATCHES_WON would be added with a corresponding lambda that extracts the appropriate value (assuming your constants, etc are defined as shown in your example): private static Dictionary<int, Func<GameInfo, string>> valueExtractors = new Dictionary<int, Func<GameInfo, string>>() { {MATCHES_WON, gi => gi.MatchesWon.ToString()}, {BULLETS_FIRED, gi => gi.BulletsFired.ToString()}, //.... etc for all value extractions }; You can then use this dictionary to extract the value in your sample method: public static List<MyResult> GetMyResult(GameInfo[] gameInfos, int input) { return gameInfo.Select(gi => new MyResult() { UserId = gi.UserId, ResultValue = valueExtractors[input](gi) }).ToList<MyResult>(); } Outside of this option, you could potentially have some sort of file/database/stored lookup with the number and the property name, then use reflection to extract the value, but that would obviously not perform as well.
I think this code is getting out of hand a bit. You're effectively using constants to index properties - and this is creating fragile code that you're looking to use some technique - such as - reflection, dictionaries, etc - to control the increased complexity. Effectively the approach that you're using now will end up with code like this: var results = GetMyResult(gameInfos, BULLETS_FIRED); The alternative is to define an extension method that lets you do this: var results = gameInfos.ToMyResults(gi => gi.BulletsFired); This is strongly-typed, it doesn't require constants, switch statements, reflection, or anything arcane. Just write these extension methods and you're done: public static class GameInfoEx { public static IEnumerable<MyResult> ToMyResults( this IEnumerable<GameInfo> gameInfos, Func<GameInfo, object> selector) { return gameInfos.Select(gi => gi.ToMyResult(selector)); } public static MyResult ToMyResult( this GameInfo gameInfo, Func<GameInfo, object> selector) { return new MyResult() { UserId = gameInfo.UserId, ResultValue = selector(gameInfo).ToString() }; } } Does that work for you?
You can use reflection for theses purposes. You can implement custom attributes, mark your properties, etc. Also, it is dynamic way to get info about your class if it changes.
If you want to manage switch code I would point you at Design Patterns book (GoF) and suggest possibly looking at patterns like Strategy and possibly Factory (thats when we talk about general case use, your case isn't very suited for Factory) and implementing them. While switch statement still has to be left somewhere after refactoring to pattern is complete (for example, in a place where you select strategy by id), code will be much more maintanable and clear. That said about general switch maintenance, if they become beast like, I am not sure its best solution given how similar your case statements look. I am 100% sure you can create some method (possibly an extension method) that will be accepting desired property accessor lambda, that should be used when results are generated.
If you want your code to be more generic, I agree with the suggestion of a dictionary or some kind of lookup pattern. You could store functions in the dictionary, but they seemly all perform the same operation - getting the value from a property. This is ripe for reflection. I'd store all your properties in a dictionary with an enum (prefer an enum to a const) as the key, and a PropertyInfo - or, less preferred, a string which describes the name of the property - as the value. You then call the GetValue() method on the PropertyInfo object to retrieve the value from the object / class. Here's an example where I'm mapping enum values to their 'same named' properties in a class, and then using reflection to retrieve the values out of a class. public enum Properties { A, B } public class Test { public string A { get; set; } public int B { get; set; } } static void Main() { var test = new Test() { A = "A value", B = 100 }; var lookup = new Dictionary<Properties, System.Reflection.PropertyInfo>(); var properties = typeof(Test).GetProperties().ToList(); foreach (var property in properties) { Properties propertyKey; if (Enum.TryParse(property.Name, out propertyKey)) { lookup.Add(propertyKey, property); } } Console.WriteLine("A is " + lookup[Properties.A].GetValue(test, null)); Console.WriteLine("B is " + lookup[Properties.B].GetValue(test, null)); } You can map your const values to the names of the properties, PropertyInfo objects which relate to those properties, functions which will retrieve the property values... whatever you think suits your needs. Of course you will need some mapping - somewhere along the way you will be depending on your input value (the const) mapping to a specific property. The method by which you can get this data might determine the best mapping structure and pattern for you.
I think the way to go is indeed some kind of mapping from one value (int) to something that is somehow a function that knows how to extract a value. If you really want to keep it extensible, so that you can easily add some without touching the code, and possibly accessing more complex properties (ie. nested properties, do some basic computation), you may want to keep that in a separate source. I think one way to do this is to rely on the Scripting Services, for instance evaluating a simple IronPython expression to extract a value... For instance in a file you could store something like : <GameStats> <GameStat name="MatchesWon" id="1"> <Expression> currentGameInfo.BulletsFired.ToString() </Expression> </GameStat> <GameStat name="FancyStat" id="2"> <Expression> currentGameInfo.SuperCoolNestedGameInfo.NestedIntValue.ToString() </Expression> </GameStat> </GameStats> and then, depending on the requested stat, you always end up retrieving the general GameInfos. You can them have some kind of foreach loop with : foreach( var gameInfo in gameInfos){ var currentGameInfo = gameInfo //evaluate the expression for this currentGameInfo return yield resultOfEvaluation } See http://www.voidspace.org.uk/ironpython/dlr_hosting.shtml for examples on how to embed IronPython Scripting in a .NET application. NOTE: when working with this kind of stuff, there are several things you must really be careful about: this potentially allows someone to inject code in your application ... you should measure the performance impact of Dynamic evaluation in here
I don't have a solution to your switch problem off the top of my head, but you could certainly reduce the code by using a class that can automatically map all the fields you need. Check out http://automapper.org/.
I would not have written the GetMyResult method in the first place. All it is doing is transforming GameInfo sequence into MyResult sequence. Doing it with Linq would be easier and more expressive. Instead of calling var myResultSequence = GetMyResult(gameInfo, MatchesWon); I would simply call var myResultSequence = gameInfo.Select(x => new MyResult() { UserId = x.UserId, ResultValue = x.MatchesWon.ToString() }); To make it more succinct you can pass the UserId and ResultValue in constructor var myResultSequence = gameInfo.Select(x => new MyResult(x.UserId, x.MatchesWon.ToString())); Refactor only if you see the selects getting duplicated too much.
This is one possible way without using reflection: using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { public class GameInfo { public int UserId { get; set; } public int MatchesWon { get; set; } public long BulletsFired { get; set; } public string LastLevelVisited { get; set; } // thousands more of these } public class MyResult { public int UserId { get; set; } // user id from above object public string ResultValue { get; set; } // one of the value fields from above with .ToString() executed on it } public enum DataType { MatchesWon = 1, BulletsFired = 2, // add more as needed } class Program { private static Dictionary<DataType, Func<GameInfo, object>> getDataFuncs = new Dictionary<DataType, Func<GameInfo, object>> { { DataType.MatchesWon, info => info.MatchesWon }, { DataType.BulletsFired, info => info.BulletsFired }, // add more as needed }; public static IEnumerable<MyResult> GetMyResult(GameInfo[] gameInfos, DataType input) { var getDataFunc = getDataFuncs[input]; return gameInfos.Select(info => new MyResult() { UserId = info.UserId, ResultValue = getDataFunc(info).ToString() }); } static void Main(string[] args) { var testData = new GameInfo[] { new GameInfo { UserId="a", BulletsFired = 99, MatchesWon = 2 }, new GameInfo { UserId="b", BulletsFired = 0, MatchesWon = 0 }, }; // you can now easily select whatever data you need, in a type-safe manner var dataToGet = DataType.MatchesWon; var results = GetMyResult(testData, dataToGet); } } }
Purely on the question of large switch statements, it is notable that there are 2 variants of the Cyclomatic Complexity metric in common use. The "original" counts each case statement as a branch and so it increments the complexity metric by 1 - which results in a very high value caused by many switches. The "variant" counts the switch statement as a single branch - this is effectively considering it as a sequence of non-branching statements, which is more in keeping with the "understandability" goal of controlling complexity.