将具有键值属性的对象 [] 映射到对象的属性,而无需使用巨大的讨厌开关
Map Object[] with Key Value properties to an object's properties without using a huge nasty switch
我有一个键值对象数组。
public class KeyValueStore
{
public string Key {get;set;}
public string Value {get;set;}
}
此数组存储我尝试填充的对象的值,如下所示:
public class Customer
{
public string Name {get;set;}
public string Country {get;set}
}
所以我想将这些键从 KeyValueStore 映射到客户属性
public Customer TransformToCustomer(KeyValueStore[] keyValueStore)
{
var customer = new Customer();
foreach (var keyValue in keyValueStore)
{
switch (keyValue.Key)
{
case "Name":
customer.Name = keyValue.Value;
break;
case "Cntry":
customer.Country = keyValue.Value;
break;
}
}
return customer;
}
有更好的方法吗?
是的,假设目标类型有一个无参数构造函数,您可以编写一个泛型方法来执行此操作:
public T CreateAndPopulate<T>(IEnumerable<KeyValueStore> propStore,
IDictionary<string, string> mapping = null)
where T:class,new()
{
T item=new T();
var type=typeof(T);
foreach(var kvs in propStore)
{
var propName = kvs.Key;
propName = mapping !=null && mapping.ContainsKey(propName)
? mapping[propName]
: propName;
var prop = type.GetProperty(propName);
if(prop == null) //does the property exist?
{
continue;
}
var propMethodInfo = prop.GetSetMethod();
if(propMethodInfo == null) //does it have a set method?
{
continue;
}
propMethodInfo.Invoke(item, new[]{ kvs.Value });
}
return item;
}
并使用它:
IEnumerable<KeyValueStore> propStore = new KeyValueStore[]{
new KeyValueStore{ Key = "Name", Value = "John" },
new KeyValueStore{ Key = "Cntry", Value = "UK" }};
var mapping = new Dictionary<string,string>{{ "Cntry", "Country" }};
var customer = CreateAndPopulate<Customer>(propStore, mapping);
我还有一个建议,很多时候大开关块表明你的对象设计中缺少一些东西,正确使用多态性可以取代开关用法。
首先,我们将重新设计 KeyValueStore class 以分离 ValueStore classes,每个都将实现通用接口 IValueStore,该接口将如下所示:
public interface IValueStore
{
void AddValueToCostumer(Customer customer);
}
现在 NameValueStore 将如下所示:
public class NameValueStore : IValueStore
{
private readonly string _name;
public NameValueStore(string name)
{
_name = name;
}
public void AddValueToCustomer(Costumer costumer)
{
customer.Name = _name;
}
}
和 CountryValueStore:
public class CountryValueStore : IValueStore
{
private readonly string _country;
public CountryNameValueStore(string country)
{
_country = country;
}
public void AddValueToCustomer(Costumer costumer)
{
customer.Country = _country;
}
}
现在您的函数 TransformToCustomer 可能如下所示:
public Customer TransformToCustomer(IValueStore[] valueStores)
{
var customer = new Customer();
foreach (var valueStore in valueStores)
{
valueStore.AddValueToCustomer(customer);
}
return customer;
}
这个解决方案对我来说感觉更 SOLID。
希望对您有所帮助!
我有一个键值对象数组。
public class KeyValueStore
{
public string Key {get;set;}
public string Value {get;set;}
}
此数组存储我尝试填充的对象的值,如下所示:
public class Customer
{
public string Name {get;set;}
public string Country {get;set}
}
所以我想将这些键从 KeyValueStore 映射到客户属性
public Customer TransformToCustomer(KeyValueStore[] keyValueStore)
{
var customer = new Customer();
foreach (var keyValue in keyValueStore)
{
switch (keyValue.Key)
{
case "Name":
customer.Name = keyValue.Value;
break;
case "Cntry":
customer.Country = keyValue.Value;
break;
}
}
return customer;
}
有更好的方法吗?
是的,假设目标类型有一个无参数构造函数,您可以编写一个泛型方法来执行此操作:
public T CreateAndPopulate<T>(IEnumerable<KeyValueStore> propStore,
IDictionary<string, string> mapping = null)
where T:class,new()
{
T item=new T();
var type=typeof(T);
foreach(var kvs in propStore)
{
var propName = kvs.Key;
propName = mapping !=null && mapping.ContainsKey(propName)
? mapping[propName]
: propName;
var prop = type.GetProperty(propName);
if(prop == null) //does the property exist?
{
continue;
}
var propMethodInfo = prop.GetSetMethod();
if(propMethodInfo == null) //does it have a set method?
{
continue;
}
propMethodInfo.Invoke(item, new[]{ kvs.Value });
}
return item;
}
并使用它:
IEnumerable<KeyValueStore> propStore = new KeyValueStore[]{
new KeyValueStore{ Key = "Name", Value = "John" },
new KeyValueStore{ Key = "Cntry", Value = "UK" }};
var mapping = new Dictionary<string,string>{{ "Cntry", "Country" }};
var customer = CreateAndPopulate<Customer>(propStore, mapping);
我还有一个建议,很多时候大开关块表明你的对象设计中缺少一些东西,正确使用多态性可以取代开关用法。
首先,我们将重新设计 KeyValueStore class 以分离 ValueStore classes,每个都将实现通用接口 IValueStore,该接口将如下所示:
public interface IValueStore
{
void AddValueToCostumer(Customer customer);
}
现在 NameValueStore 将如下所示:
public class NameValueStore : IValueStore
{
private readonly string _name;
public NameValueStore(string name)
{
_name = name;
}
public void AddValueToCustomer(Costumer costumer)
{
customer.Name = _name;
}
}
和 CountryValueStore:
public class CountryValueStore : IValueStore
{
private readonly string _country;
public CountryNameValueStore(string country)
{
_country = country;
}
public void AddValueToCustomer(Costumer costumer)
{
customer.Country = _country;
}
}
现在您的函数 TransformToCustomer 可能如下所示:
public Customer TransformToCustomer(IValueStore[] valueStores)
{
var customer = new Customer();
foreach (var valueStore in valueStores)
{
valueStore.AddValueToCustomer(customer);
}
return customer;
}
这个解决方案对我来说感觉更 SOLID。
希望对您有所帮助!