|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionSince C# 3.0, we've had the chance to play around with anonymous types among other cool new stuff. Since I've starting working with them, I found anonymous types to be useful in more situations than just LINQ statements. For example, in JavaScript many functions may return associative arrays, and in the same way in C#, a method that may return an anonymously typed object when declaring a whole class or struct is overkill. That's not possible, Microsoft says. You can't pass an anonymous type from one method to the next, down the call stack. In fact, you can pass anonymous types down the call stack. You'll just have to (implicitly) cast them back to an BackgroundA fellow who goes by the name AlexJ (kudos for the idea!) has found otherwise. Basically what we need to do is take the returned anonymous type and cast it back to its original form. For this, he uses a generic extension method on public static T CastByExample<T>(this object obj, T example) {
return (T) obj;
}
Say we've got a method that returns some anonymous type that has a property var v = obj.CastByExample(new { FullName = "" });
The great disadvantage of this approach is that when the number of properties, names of properties and types of properties of A Better, More Flexible ApproachI wanted to get rid of as much of the required coordination between the method that returns an anonymous My design has three advantages over the initial approach:
If a property in the resulting anonymous type cannot be set because it doesn't exist in the source So without further ado, here's the code: public static T TolerantCast<T>(this object o, T example) where T: class {
IComparer<string> comparer = StringComparer.CurrentCultureIgnoreCase;
//Get constructor with lowest number of parameters and its parameters
var constructor = typeof(T).GetConstructors(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
).OrderBy(c => c.GetParameters().Length).First();
var parameters = constructor.GetParameters();
//Get properties of input object
var sourceProperties = new List<PropertyInfo>(o.GetType().GetProperties());
if (parameters.Length > 0) {
var values = new object[parameters.Length];
for (int i = 0; i < values.Length; i++) {
Type t = parameters[i].ParameterType;
//See if the current parameter is found as a property in the input object
var source = sourceProperties.Find(delegate(PropertyInfo item) {
return comparer.Compare(item.Name, parameters[i].Name) == 0;
});
//See if the property is found, is readable, and is not indexed
if (source != null && source.CanRead &&
source.GetIndexParameters().Length == 0) {
//See if the types match.
if (source.PropertyType == t) {
//Get the value from the property in the input object and save it for use
//in the constructor call.
values[i] = source.GetValue(o, null);
continue;
}
else {
//See if the property value from the input object can be converted to
//the parameter type
try {
values[i] = Convert.ChangeType(source.GetValue(o, null), t);
continue;
}
catch {
//Impossible. Forget it then.
}
}
}
//If something went wrong (i.e. property not found, or property isn't
//converted/copied), get a default value.
values[i] = t.IsValueType ? Activator.CreateInstance(t) : null;
}
//Call the constructor with the collected values and return it.
return (T) constructor.Invoke(values);
}
//Call the constructor without parameters and return the it.
return (T) constructor.Invoke(null);
}
A Final NoteAnonymous types are in no way a golden hammer. Only use them when you see the alternative being totally overkill. Since this approach uses reflection quite heavily, you shouldn't be using this method if CPU utilization is a bottleneck. History
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||