这是我今天写的代码。它通过了简单的测试。我想你可以用它来开始。
这是测试:
/// <summary>
///A test for MapProperties
///</summary>
public void MapPropertiesTestHelper<S, T>(S source, T target1, bool failIfNotMatched)
{
Cake.Common.Mapper<S, T> target = new Cake.Common.Mapper<S, T>();
target.MapProperties(source, target1, failIfNotMatched);
}
[TestMethod()]
public void MapPropertiesTest()
{
var source = new Source {
OneTwo = 10,
ThreeFour = "foo"
};
var target = new Target {
OneTwo = 10,
ThreeFour = "bar"
};
MapPropertiesTestHelper<Source, Target>(source, target, true);
Assert.AreEqual(source.OneTwo, target.OneTwo);
Assert.AreEqual(source.ThreeFour, target.ThreeFour);
Assert.AreEqual(source.five_six, target.FiveSix);
}
public class Source
{
public int OneTwo { get; set; }
public string ThreeFour { get; set; }
public bool five_six { get; set; }
}
public class Target
{
public int OneTwo { get; set; }
public string ThreeFour { get; set; }
public bool FiveSix { get; set; }
}
这是代码:
public class MapperItem
{
public MapperItem(MemberInfo member, object o)
{
this.Member = member;
this.Object = o;
}
public MemberInfo Member
{
get;
set;
}
public object Object
{
get;
set;
}
public Type Type
{
get
{
return this.Member.UnderlyingType();
}
}
public object Value
{
get
{
if (this.Member is PropertyInfo)
{
return (this.Member as PropertyInfo).GetValue(this.Object, null);
}
else if (this.Member is FieldInfo)
{
return (this.Member as FieldInfo).GetValue(this.Object);
}
else
{
throw new Exception("sourceMember must be either PropertyInfo or FieldInfo");
}
}
}
public object Convert(Type targetType)
{
object converted = null;
if (this.Value == null)
{
return converted;
}
else if (targetType.IsAssignableFrom(this.Type))
{
converted = this.Value;
}
else
{
var conversionKey = Tuple.Create(this.Type, targetType);
if (Conversions.ContainsKey(conversionKey))
{
converted = Conversions[conversionKey](this.Value);
}
else
{
throw new Exception(targetType.Name + " is not assignable from " + this.Type.Name);
}
}
return converted;
}
public void Assign(object value)
{
if (this.Member is PropertyInfo)
{
(this.Member as PropertyInfo).SetValue(this.Object, value, null);
}
else if (this.Member is FieldInfo)
{
(this.Member as FieldInfo).SetValue(this.Object, value);
}
else
{
throw new Exception("destinationMember must be either PropertyInfo or FieldInfo");
}
}
public static Dictionary<Tuple<Type, Type>, Func<object, object>> Conversions = new Dictionary<Tuple<Type, Type>, Func<object, object>>();
}
public class Mapper<S, T>
{
private List<string> ignoreList = new List<string>();
public List<string> IgnoreList
{
get { return ignoreList; }
set { ignoreList = value; }
}
public void MapProperties(S source, T target, bool failIfNotMatched = true)
{
foreach (PropertyInfo property in source.GetType()
.GetProperties()
.Where(c => !IgnoreList.Contains(c.Name)))
{
try
{
var sourceField = new MapperItem(property, source);
var targetField = new MapperItem(MatchToTarget(property), target);
targetField.Assign(sourceField.Convert(targetField.Type));
}
catch (TargetNotMatchedException noMatch)
{
if (failIfNotMatched)
{
throw noMatch;
}
}
}
}
private MemberInfo MatchToTarget(MemberInfo member)
{
List<MemberInfo> members = new List<MemberInfo>();
members.AddRange(typeof(T).GetProperties());
members.AddRange(typeof(T).GetFields());
var exactMatch = from c in members where c.Name == member.Name select c;
if (exactMatch.FirstOrDefault() != null)
{
return exactMatch.First();
}
var sameAlphaChars = from c in members
where NormalizeName(c.Name) == NormalizeName(member.Name)
select c;
if (sameAlphaChars.FirstOrDefault() != null)
{
return sameAlphaChars.First();
}
throw new TargetNotMatchedException(member, typeof(T));
}
private static string NormalizeName(string input)
{
string normalized = input.Replace("_", "").ToUpper();
return normalized;
}
}
public class TargetNotMatchedException : Exception
{
public TargetNotMatchedException(MemberInfo member, Type type)
: base("no match for member named " + member.Name + " with type named " + type.Name)
{
this.Member = member;
this.Type = type;
}
public MemberInfo Member { get; set; }
public Type Type { get; set; }
}
public static class ReflectionExtensions
{
public static Type UnderlyingType(this MemberInfo member)
{
Type type;
switch (member.MemberType)
{
case MemberTypes.Field:
type = ((FieldInfo)member).FieldType;
break;
case MemberTypes.Property:
type = ((PropertyInfo)member).PropertyType;
break;
case MemberTypes.Event:
type = ((EventInfo)member).EventHandlerType;
break;
default:
throw new ArgumentException("MemberInfo must be if type FieldInfo, PropertyInfo or EventInfo", "member");
}
return Nullable.GetUnderlyingType(type) ?? type;
}
}