C#跨项目扩展类属性:在引用项目中扩展源类的实现方法
跨项目扩展类属性的几种实现方案
针对你在项目2中扩展项目1类属性的需求,以下是几种实用的实现方案,覆盖不同场景:
1. 继承(最直观的方案)
如果项目1中的类不是密封类(sealed),可以直接在项目2中继承这些类并添加新属性。需要注意的是,若原数据是项目1类的实例,需转换为子类实例才能使用扩展属性。
示例代码
项目1(ProjectA):
public class ClassB { public int Id { get; set; } public string SomeField { get; set; } } public class ClassC { public string Code { get; set; } public DateTime CreateTime { get; set; } }
项目2(ProjectB):
// 扩展ClassB public class ClassBExtended : ProjectA.ClassB { public string Name { get; set; } public string Phone { get; set; } } // 扩展ClassC public class ClassCExtended : ProjectA.ClassC { public string Remark { get; set; } public bool IsValid { get; set; } }
使用方式
将原项目的实例转换为扩展类实例后,即可同时操作原字段和新属性:
// 假设原方法返回项目1的ClassB列表 List<ProjectA.ClassB> originalData = await SomeFunction(); // 转换为扩展类列表 List<ClassBExtended> extendedData = originalData.Select(b => new ClassBExtended { Id = b.Id, SomeField = b.SomeField, Name = "默认名称", Phone = "123456" }).ToList(); // 操作原字段与扩展属性 extendedData.ForEach(x => { x.Id = 1; x.SomeField = "更新后的值"; x.Name = "新名称"; });
2. 装饰器模式(灵活包装原实例)
如果项目1的类是密封类,或不想用继承,可以用装饰器模式:创建包装类持有原类实例,同时扩展新属性,直接访问原类的所有成员。
示例代码(项目2)
public class ClassBDecorator { // 持有原类实例 public ProjectA.ClassB Original { get; } // 扩展属性 public string Name { get; set; } public string Phone { get; set; } public ClassBDecorator(ProjectA.ClassB original) { Original = original ?? throw new ArgumentNullException(nameof(original)); } } public class ClassCDecorator { public ProjectA.ClassC Original { get; } public string Remark { get; set; } public bool IsValid { get; set; } public ClassCDecorator(ProjectA.ClassC original) { Original = original ?? throw new ArgumentNullException(nameof(original)); } }
使用方式
List<ProjectA.ClassB> originalData = await SomeFunction(); // 包装原实例 List<ClassBDecorator> decoratedData = originalData.Select(b => new ClassBDecorator(b) { Name = "默认名称", Phone = "123456" }).ToList(); // 操作原字段与扩展属性 decoratedData.ForEach(x => { x.Original.Id = 1; x.Original.SomeField = "更新后的值"; x.Name = "新名称"; });
3. 模拟扩展属性(无额外类实现)
C#没有原生扩展属性,但可以通过扩展方法+弱引用字典模拟,适合不想创建新类的场景,同时避免内存泄漏。
示例代码(项目2)
using System.Collections.Concurrent; using System.WeakReference; // 为ClassB模拟扩展属性 public static class ClassBExtensions { // 用弱引用字典存储扩展属性,避免内存泄漏 private static readonly ConcurrentDictionary<WeakReference<ProjectA.ClassB>, (string Name, string Phone)> _extendedProps = new(); public static string GetName(this ProjectA.ClassB obj) { if (_extendedProps.TryGetValue(GetWeakRef(obj), out var props)) return props.Name; return null; } public static void SetName(this ProjectA.ClassB obj, string value) { var weakRef = GetWeakRef(obj); if (_extendedProps.TryGetValue(weakRef, out var props)) _extendedProps[weakRef] = (value, props.Phone); else _extendedProps[weakRef] = (value, null); } public static string GetPhone(this ProjectA.ClassB obj) { if (_extendedProps.TryGetValue(GetWeakRef(obj), out var props)) return props.Phone; return null; } public static void SetPhone(this ProjectA.ClassB obj, string value) { var weakRef = GetWeakRef(obj); if (_extendedProps.TryGetValue(weakRef, out var props)) _extendedProps[weakRef] = (props.Name, value); else _extendedProps[weakRef] = (null, value); } private static WeakReference<ProjectA.ClassB> GetWeakRef(ProjectA.ClassB obj) { // 查找已存在的弱引用,避免重复创建 foreach (var kvp in _extendedProps.Keys) { if (kvp.TryGetTarget(out var target) && target == obj) return kvp; } return new WeakReference<ProjectA.ClassB>(obj); } }
使用方式
List<ProjectA.ClassB> data = await SomeFunction(); data.ForEach(x => { // 操作原字段 x.Id = 1; x.SomeField = "测试值"; // 设置/获取扩展属性 x.SetName("张三"); x.SetPhone("138xxxxxxx"); var userName = x.GetName(); });
4. 匿名类型/ValueTuple(临时场景快速实现)
如果只是临时需要携带额外属性,不需要长期维护,可以用匿名类型或ValueTuple包装原对象:
List<ProjectA.ClassB> originalData = await SomeFunction(); // 使用ValueTuple(可修改属性) var extendedData = originalData.Select(b => (Original: b, Name: "默认名称", Phone: "123456")).ToList(); extendedData.ForEach(x => { x.Original.Id = 1; x.Original.SomeField = "更新后的值"; x.Name = "新名称"; // ValueTuple的属性可修改 });
内容的提问来源于stack exchange,提问作者NajiMakhoul
相关产品推荐
相关产品推荐

