要传递匿名类型,请考虑使用
dynamic。下面展示了一个更长的示例和您可以使用的技术。例如,考虑在此处称TreadSafeDynamicObject为“CustomEmployee”,以使代码更有意义。该代码具有接受动态对象(您的匿名、可能嵌套的类)的构造函数,例如:
var someCustomEmploye = new {
IsIntern = false,
EmployeeFacts = new {
IsSenior = true,
BirthDate = new DateTime(1960, 1, 1)
}
};
您可以使用下面显示的技术将someCustomEmployee转换为动态对象,例如将其传递到构造函数中,在我的代码中它将是:
dynamic someEmp = new ThreadSafeDynamicObject(someCustomEmployee)
一旦您将someEmp转换为正确的动态对象,您的LogEmployee函数可以例如将该对象序列化并记录它或以其他方式处理它(请注意,如果仅需要序列化匿名类实例,则不必通过将其转换为动态对象来进行操作)。
示例:
dynamic threadSafeToyota = new ThreadSafeDynamicObject(new {
Make = "Toyota",
Model = "CR-H",
Propulsion = new {
IsHybrid = true,
UsesPetrol = true,
ElectricMotor = true
}
});
这段代码接受一个动态对象,并使用私有方法'ToDictionary'将提供的匿名类实例的对象图转换为字典,作为设置动态对象属性的另一种替代方式。
请注意,我在这里添加了一些代码以提供线程安全性,用于设置和获取属性。
public class ThreadSafeDynamicObject : DynamicObject, IEnumerable<KeyValuePair<string, object>>
{
public ThreadSafeDynamicObject()
{
}
public ThreadSafeDynamicObject(dynamic members)
{
var membersDict = ToDictionary(members);
InitMembers(membersDict);
}
private IDictionary<string, object> ToDictionary(object data)
{
var attr = BindingFlags.Public | BindingFlags.Instance;
var dict = new Dictionary<string, object>();
foreach (var property in data.GetType().GetProperties(attr))
{
if (property.CanRead)
{
dict.Add(property.Name, property.GetValue(data, null));
}
}
return dict;
}
private void InitMembers(IDictionary<string, object> membersDict)
{
foreach (KeyValuePair<string, object> member in membersDict){
_members.AddOrUpdate(member.Key, member.Value, (key, oldValue) => member.Value);
}
}
private readonly ConcurrentDictionary<string, object> _members = new();
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
return _members.TryGetValue(binder.Name, out result);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
_members.AddOrUpdate(binder.Name, value, (key, oldvalue) => value);
return true;
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return _members.Keys.ToList().AsReadOnly();
}
public override string ToString()
{
return JsonSerializer.Serialize(_members);
}
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
return _members.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _members.GetEnumerator();
}
}
当在Linqpad 7中运行代码时,我得到了以下输出(我正在使用静态System.Console并在此处使用System.Dynamic):
WriteLine(threadSafe.ToString());
WriteLine(threadSafe.Make);
WriteLine(threadSafe.Model);
WriteLine(threadSafe.Propulsion.IsHybrid);
WriteLine(threadSafe.Propulsion.UsesPetrol);
WriteLine(threadSafe.Propulsion.ElectricMotor);
这样做有几个优点。它支持嵌套级别,正如您在输出中所看到的,并且非常灵活。这里的关键是使用'ToDictionary'方法。此外,我们不必在.NET框架之外使用其他库,因此功能已内置。我没有检查此代码的所有变体,但至少支持匿名类型对象图的典型场景。
关键在于首先将匿名类型转换为字典,然后使用派生的DynamicObject的成员('fields'或'props')填充内部并发字典。
有几种解决方法:
您可以进行装箱。例如,编写一个接受对象的方法,使用反射提取属性并记录属性及其值,例如:
public void LogEmployees(object someCustomEmployee) { // .. }
您可以像我的示例一样将匿名对象转换为动态对象
除了装箱或转换为动态对象之外,您还可以通过序列化转换后的对象(包括装箱的对象或动态变量)来避免反射。