C#中的动态类型允许开发者在编译阶段跳过类型检查,在运行时才解析类型相关的操作,这种特性非常适合处理结构不确定的对象场景。动态对象则可以通过继承特定基类实现属性的动态扩展,配合对象合并与Map功能,能大幅提升代码的灵活性。

C#动态类型与动态对象基础
动态类型的使用
C# 4.0引入了dynamic关键字,声明为dynamic的变量会在运行时进行类型解析,不需要提前定义具体的类型结构。我们可以通过以下示例创建简单的动态类型变量:
using System;
class Program
{
static void Main()
{
// 声明动态类型变量
dynamic dynamicVar = 10;
Console.WriteLine(dynamicVar);
// 动态修改变量类型
dynamicVar = "hello dynamic";
Console.WriteLine(dynamicVar);
dynamicVar = new { Name = "test", Age = 20 };
Console.WriteLine(dynamicVar.Name);
}
}创建自定义动态对象
如果要创建可以动态添加属性的对象,需要继承DynamicObject类,并重写相关方法。以下示例实现了一个支持动态添加属性的动态对象:
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Reflection;
public class CustomDynamicObject : DynamicObject
{
// 存储动态属性的字典
private readonly Dictionary<string, object> _properties = new Dictionary<string, object>();
// 获取动态属性值
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
return _properties.TryGetValue(binder.Name, out result);
}
// 设置动态属性值
public override bool TrySetMember(SetMemberBinder binder, object value)
{
_properties[binder.Name] = value;
return true;
}
// 获取所有动态属性
public Dictionary<string, object> GetAllProperties()
{
return new Dictionary<string, object>(_properties);
}
}合并两个动态对象
合并两个动态对象的核心思路是将两个对象的所有属性提取出来,合并到一个新的动态对象中,如果存在同名属性,可以根据需求选择覆盖或者保留原值。以下是完整的合并实现代码:
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Reflection;
public class ObjectMergeHelper
{
// 合并两个动态对象,同名属性优先使用第二个对象的属性值
public static CustomDynamicObject MergeDynamicObjects(CustomDynamicObject obj1, CustomDynamicObject obj2)
{
var mergedObj = new CustomDynamicObject();
var obj1Properties = obj1.GetAllProperties();
var obj2Properties = obj2.GetAllProperties();
// 先添加第一个对象的所有属性
foreach (var prop in obj1Properties)
{
((dynamic)mergedObj).GetType().GetMethod("TrySetMember")?.Invoke(mergedObj, new object[] { new CustomBinder(prop.Key), prop.Value });
// 使用反射调用TrySetMember更通用,也可以直接通过dynamic赋值
dynamic dynamicMerged = mergedObj;
dynamicMerged.GetType().GetProperty("Item")?.SetValue(dynamicMerged, prop.Value, new object[] { prop.Key });
}
// 再添加第二个对象的属性,覆盖同名属性
foreach (var prop in obj2Properties)
{
dynamic dynamicMerged = mergedObj;
dynamicMerged.GetType().GetProperty("Item")?.SetValue(dynamicMerged, prop.Value, new object[] { prop.Key });
}
return mergedObj;
}
// 简易绑定器,用于反射调用设置属性方法
private class CustomBinder : GetMemberBinder
{
public CustomBinder(string name) : base(name, false) { }
public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion)
{
return errorSuggestion;
}
}
}
// 测试合并功能
class TestMerge
{
static void Main()
{
dynamic obj1 = new CustomDynamicObject();
obj1.Name = "张三";
obj1.Age = 25;
dynamic obj2 = new CustomDynamicObject();
obj2.Age = 30;
obj2.Address = "北京";
var merged = ObjectMergeHelper.MergeDynamicObjects(obj1, obj2);
dynamic mergedDynamic = merged;
Console.WriteLine($"姓名:{mergedDynamic.Name},年龄:{mergedDynamic.Age},地址:{mergedDynamic.Address}");
// 输出结果:姓名:张三,年龄:30,地址:北京
}
}实现Map功能示例
Map功能通常指将一个对象的属性按照指定规则映射到另一个对象中,以下实现了一个通用的Map方法,支持将源对象的属性映射到目标对象:
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Reflection;
public class MapHelper
{
// 将源对象的属性映射到目标动态对象
public static CustomDynamicObject MapToDynamic(object source, Dictionary<string, string> propertyMapping = null)
{
var target = new CustomDynamicObject();
var sourceType = source.GetType();
var sourceProperties = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var sourceProp in sourceProperties)
{
// 如果有映射规则,使用映射后的属性名,否则使用原属性名
string targetPropName = propertyMapping != null && propertyMapping.ContainsKey(sourceProp.Name)
? propertyMapping[sourceProp.Name]
: sourceProp.Name;
var value = sourceProp.GetValue(source);
dynamic dynamicTarget = target;
// 设置目标对象的动态属性
dynamicTarget.GetType().GetProperty("Item")?.SetValue(dynamicTarget, value, new object[] { targetPropName });
}
return target;
}
// 将动态对象映射到有固定类型的目标对象
public static T MapToObject<T>(CustomDynamicObject source) where T : new()
{
var target = new T();
var targetProperties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
var sourceProperties = source.GetAllProperties();
foreach (var targetProp in targetProperties)
{
if (sourceProperties.ContainsKey(targetProp.Name) && targetProp.CanWrite)
{
var value = sourceProperties[targetProp.Name];
// 处理类型转换
if (value != null && targetProp.PropertyType.IsAssignableFrom(value.GetType()))
{
targetProp.SetValue(target, value);
}
}
}
return target;
}
}
// 测试Map功能
class TestMap
{
static void Main()
{
// 源对象
var sourceObj = new { UserName = "李四", UserAge = 28, UserEmail = "test@ipipp.com" };
// 属性映射规则:源属性名 -> 目标属性名
var mapping = new Dictionary<string, string>
{
{ "UserName", "Name" },
{ "UserAge", "Age" }
};
// 映射到动态对象
var dynamicResult = MapHelper.MapToDynamic(sourceObj, mapping);
dynamic dynamicResultDynamic = dynamicResult;
Console.WriteLine($"Name:{dynamicResultDynamic.Name},Age:{dynamicResultDynamic.Age},Email:{dynamicResultDynamic.UserEmail}");
// 定义目标固定类型
public class UserInfo
{
public string Name { get; set; }
public int Age { get; set; }
}
// 动态对象映射到固定类型对象
var userInfo = MapHelper.MapToObject<UserInfo>(dynamicResult);
Console.WriteLine($"Name:{userInfo.Name},Age:{userInfo.Age}");
}
}注意事项
使用动态类型时需要注意,动态类型的操作在编译阶段不会进行类型检查,如果调用了不存在的属性或方法,会在运行时抛出RuntimeBinderException,因此建议在使用动态对象前做好属性存在性的校验。另外,动态对象的性能比静态类型略低,如果不是必要场景,不建议过度使用动态类型。
合并对象时如果属性类型不一致,需要做额外的类型转换处理,否则可能会出现赋值异常。Map功能的实现可以根据实际需求扩展,比如支持更多类型的转换、支持嵌套对象的映射等。