Is it possible to get value of a method thru Property - c#

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

Related

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.

Reflection to get and use class properties

I am trying to update a linked list from a datagridview using reflection so I don't have to write a line of code for each property.
The class:
public class clsUnderlying
{
public int UnderlyingID { get; set; }
public string Symbol { get; set; }
public double RiskFreeRate { get; set; }
public double DividendYield { get; set; }
public DateTime? Expiry { get; set; }
}
One line of code per property works:
UdlyNode.Symbol = (string)GenericTable.Rows[IDX].Cells["Symbol"].Value;
UdlyNode.Expiry = (DateTime)GenericTable.Rows[IDX].Cells["Expiry"].Value;
etc.
But there are many classes and class properties, so I'd prefer to use a loop and reflection, but I'm not sure how, and my attempt below has errors.
PropertyInfo[] classProps = typeof(GlobalVars.clsUnderlying).GetProperties();
foreach (var Prop in classProps)
{
Type T = GetType(Prop); // no overload for method GetType
UdlyNode.Prop.Name = Convert.T(GenericTable.Rows[IDX].Cells[Prop.Name].Value); // error on "Prop.Name" and "T.("
}
Thanks for any suggestions or links to further my understanding.
Reflection-based loop needs to use a different syntax:
Property type is a property of PropertyInfo,
Convert has a ChangeType method that takes System.Type, and
Property assignment needs to be done by calling SetValue
Therefore, your loop would look like this:
foreach (var p in classProps) {
p.SetValue(
UdlyNode
, Convert.ChangeType(
GenericTable.Rows[IDX].Cells[p.Name].Value
, p.PropertyType
)
);
}
I would suggest to use BindingSource. This way a changed value in the Grid will automatically be changed in your list:
BindingSource bs = new BindingSource();
bs.DataSource = yourList;
dataGridView1.DataSource = bs;
This would solve the case where you want to update values manually changed in the grid.

Uri.UnescapeDataString all properties on an object?

I have some objects where some of the properties a URLEncoded.
What would be the fastest way to Uri.UnescapeDataString all properties on an object?
Not clear what you're going to reach. Just assuming you want to automate the process of decode and do not want to call each one property separately and decode it.
The way to iterate through class properties is reflection. Actually, reflection never was fast. But it's also not clear, what you mean by 'fastest'.
Here is a short sample how to achieve it with reflection:
public class DecoderTests
{
public String OneItem { get; set; }
public String SecondItem { get; set; }
public String ThirdClean { get; set; }
}
class Program
{
static void Main(string[] args)
{
var ddd = Uri.EscapeUriString("Http://google tes.com");
var decod = new DecoderTests
{
OneItem = ddd.ToString(),
SecondItem = ddd.ToString(),
ThirdClean = "clean"
};
PropertyInfo[] properties = typeof(DecoderTests).GetProperties();
foreach (PropertyInfo property in properties)
{
var current = property.GetValue(decod) as String;
if (!String.IsNullOrEmpty(current))
{
property.SetValue(decod, Uri.UnescapeDataString(current));
}
}
}
}
Or just call each property separately and decode it. All the alternatives for Uri.UnescapeDataString like Regex.Unescape or HttpUtility.UrlDecode will perform much slower.

How can I refactor this C# code

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.

Recurse through properties of a class

Take this sample class as an example:
[AttributeUsage(AttributeTargets.All, AllowMultiple=true)]
public class BugFixAttribute : System.Attribute
{
public int BugId { get; private set; }
public string Programmer { get; private set; }
public DateTime Date { get; private set; }
public string Comments { get; set; }
public string RefersTo { get; set; }
public BugFixAttribute(int bugId = 0, string programmer = "")
{
this.BugId = bugId;
this.Programmer = programmer;
Date = DateTime.Now;
}
}
And I want to recuse through the properties to use like:
object[] attr = info.GetCustomAttributes(typeof(BugFixAttribute), false);
foreach (object attribute in attr)
{
BugFixAttribute bfa = (BugFixAttribute) attribute;
Debug.WriteLine(string.Format("\nBugId: {0}", bfa.BugId));
Debug.WriteLine(string.Format("Programmer: {0}", bfa.Programmer));
//...
}
Because what I need to do is to print these to a file. So how can I recurse through the properties instead of doing the Debug.WriteLine() through all of them, is there a way or do I have to write it out.
I would suggest that this is probably not a great use for Attributes, as it muddies up the meta attached to the code. If you want to standardize a way to get at this sort of information regarding bug fixes, I would suggest coming up with an XML Comment Tag, and then turning on XML Comments for your project, and using that instead.
Example Syntax:
/// <summary>This Method Does Something</summary>
/// <BugFix BugId="1234" Programmer="Bob" Date="2/1/2010">Fix Comments</BugFix>
public void MyMethod()
{
// Do Something
}
Yes, if you use reflection:
Type t = bfa.GetType();
PropertyInfo[] properties = t.GetProperties();
foreach(var prop in properties)
{
Debug.WriteLine(string.Format("{0}: {1}", prop.Name,prop.GetValue(bfa,null)));
}
This will print the name and value of all public properties in bfa. You can check the CanRead property on a PropertyInfo to check if it can be read (ie. if it declares a getter). The example will fail if one of the properties are read-only or is indexed - if this can occur, you need to check for it in the code.
I love Linq for this kind of thing
var props = from b in info.GetCustomAttributes(typeof(BugFixAttribute), false)
from p in b.GetType().GetProperties()
select new {
Name = p.Name,
Value = p.GetValue(p.GetValue(b, null))
};
foreach(var prop in props)
{
Debug.WriteLine(string.Format("{0}: {1}", prop.Name, prop.Value));
}
If I read the question correctly, you're looking for a simpler way to write out the class information, right? You've got two options:
reflection, for a highly generic solution that will prolly output way too much information (assuming this is java - or .NET which I've been told is very much the same...)
define the toString() method, and call Debug.WriteLine(bfa)
Solution 1 is probably way overkill. You'll probably get output for stuff you don't want, and you'll not be able to get private values.
Solution 2 is the simple way.
public class BugFixAttribute : System.Attribute
{
...
public String toString(){
return string.Format("\nBugId: {0}\nProgrammer: {1}", this.BugId, this.Programmer));
}
}
foreach (var (BugFixAttribute)attribute in attr)
{
foreach(PropertyInfo prop in attribute.GetType().GetProperties())
{
Debug.WriteLine(string.Format("{0}: {1}", prop.name,prop.GetValue(attribute,null));
}
}

Categories