我有一个Dictionary<string, object>
,它将属性名称保存为字符串,并将其值作为对象。我还有一个Bind
方法扩展,通过反射,将该属性名称设置为相应的值:
public static T Bind<T>(this T @this,
Dictionary<string, object> newValues,
params string[] exceptions) where T : class
{
var sourceType = @this.GetType();
foreach (var pair in newValues.Where(v => !exceptions.Contains(v.Key)))
{
var property = sourceType.GetProperty(pair.Key,
BindingFlags.SetProperty |
BindingFlags.Public |
BindingFlags.Instance);
var propType = Nullable.GetUnderlyingType(property.PropertyType) ??
property.PropertyType;
property.SetValue(@this, (pair.Value == null) ? null :
Convert.ChangeType(pair.Value, propType), null);
}
return @this;
}
例如,考虑这样一个类:
public class User
{
public string Name { get; set; }
public DateTime Date { get; set; }
}
一切都运行正常,除非我得到一个具有另一个对象的属性名称的类,如下所示:
public class User
{
public string Name { get; set; }
public DateTime Date { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string PostalCode { get; set; }
}
所以,如果我尝试发送Name
属性名称,那么好,但是我遇到了复合属性名称的问题,比如Address.PostalCode
。
你能告诉一种处理这种情况的方法吗?
编辑#1:
总结问题:在sourceType.GetProperty("Name", ...)
类实例的上下文中正确调用User
允许设置其值,但在同一实例中使用sourceType.GetProperty("Address.PostalCode", ...)
不起作用。
编辑#2: 一个更完整的例子应该是:
var user = new User{ Address = new Address() };
var values = new Dictionary<string, object>
{
{ "Name" , "Sample" },
{ "Date" , DateTime.Today },
{ "Address.PostalCode", "12345" } // Here lies the problem
}
user.Bind(values);
答案 0 :(得分:0)
我的猜测是Convert.ChangeType
仅适用于实现IConvertible
的对象。因此,我只需添加一个检查,并且仅在Convert.ChangeType
具有实现IConvertible的类型时才使用pair.Value
。此外,afaik Convert不使用重载转换运算符,因此每当pair.Value不是结构时,您都可以保存此检查,即
object value;
if (pair.Value == null) {
value = null;
} else {
value = pair.Value.GetType().IsStruct ? Convert.ChangeType(pair.Value, propType) : pair.Value;
}
...
答案 1 :(得分:0)
核心.NET中有许多绑定引擎,WPF,ASP.NET MVC,winforms,谁知道其他多少,你可以查看所有源代码和文档关于他们的语法。
让我们看看最简单的案例。假设变量X包含一个对象,并且您具有绑定表达式“A.B.C”。让我们分开绑定路径,第一部分是“A”。因此,您使用反射来获取X中名为“A”的属性,并将其他对象放入X.现在是第二部分“B”,所以让我们在(新)X中找到名为“B”的属性。你找到了,然后把它放到X.现在你到达最后一部分,“C”,现在你可以在X中读取或写入该属性。关键是你不需要递归或任何东西,它只是一个简单的循环,你迭代绑定表达式的各个部分,评估它们,并将当前对象保存在同一个变量中。
但事实是它可以变得更加复杂。您可以要求进行数组索引,例如“A.B [2] .C”。或者如果你有一个路径“A.B”,而X.A为空,你会怎么做?实例化X.A,但如果缺少公共无参数构造函数会怎样?
我希望你看到它可能是一个非常复杂的问题。您必须指定语法和规则,然后实现它。您没有在问题中指定要使用的确切语法和规则。如果它碰巧不仅仅是我上面提到的简单案例,那么解决方案可能过于冗长。
答案 2 :(得分:0)
我能够解决它,确定属性名称是否有句号并重复出现:
public static T Bind<T>(this T @this,
Dictionary<string, object> newValues,
params string[] exceptions) where T : class
{
var sourceType = @this.GetType();
var binding = BindingFlags.Public | BindingFlags.Instance;
foreach (var pair in newValues.Where(v => !exceptions.Contains(v.Key)))
{
if(pair.Key.Contains("."))
{
var property = sourceType.GetProperty(
pair.Key.Split('.').First(),
binding | BindingFlags.GetProperty);
var value = property.GetValue(@this, null);
value.Bind(new Dictionary<string, object>
{
{
String.Join(".", pair.Key.Split('.').Skip(1).ToArray()),
pair.Value
}
});
}
else
{
var property = sourceType.GetProperty(pair.Key,
binding | BindingFlags.SetProperty);
var propType = Nullable.GetUnderlyingType(property.PropertyType) ??
property.PropertyType;
property.SetValue(@this, (pair.Value == null) ? null :
Convert.ChangeType(pair.Value, propType), null);
}
}
return @this;
}
用法:
var user = new User {Address = new Address{ User = new User() }};
var values = new Dictionary<string, object>()
{
{"Name", "Sample"},
{"Date", DateTime.Today},
{"Address.PostalCode", "12345"},
{"Address.User.Name", "Sub Sample"}
};
user.Bind(values);
public class User
{
public string Name { get; set; }
public DateTime Date { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string PostalCode { get; set; }
public User User { get; set; }
}