C# 常用拓展

字符串拓展

		public static bool IsEmpty(this string s) => string.IsNullOrEmpty(s);

        public static bool IsBlank(this string s) => string.IsNullOrWhiteSpace(s);

        public static string EmptyToDefault(this string s, string defStr) => s.IsEmpty() ? defStr : s;

		/// <summary>
        /// 替换匹配第一个
        /// </summary>
        /// <param name="s"></param>
        /// <param name="oldStr"></param>
        /// <param name="newStr"></param>
        /// <param name="right">从右到左</param>
        /// <returns></returns>
        public static string ReplaceOnce(this string s, string oldStr, string newStr, bool right = false) {
            if (s.IsEmpty() || oldStr.IsEmpty() || !s.Contains(oldStr)) {
                return s;
            }

            int indexOf = right ? s.LastIndexOf(oldStr) : s.IndexOf(oldStr);
            string first = s.Substring(0, indexOf);
            string last = "";
            if (indexOf + oldStr.Length < s.Length) {
                last = s.Substring(indexOf + oldStr.Length);
            }

            return first + newStr + last;
        }

        /// <summary>
        /// 格式化(java) "123{}456{}789",["a","bcd"] => "123a456bcd789"
        /// </summary>
        /// <param name="s"></param>
        /// <param name="pStrings"></param>
        /// <returns></returns>
        public static string Format(this string s, params string[] pStrings) {
            if (s.IsEmpty() || pStrings.Length == 0) {
                return s;
            }

            int i = 0;
            while (s.Contains("{}")) {
                if (i >= pStrings.Length) {
                    break;
                }

                s = s.ReplaceOnce("{}", pStrings[i]);
                i++;
            }

            return s;
        }

数组列表拓展

		public static string Join<T>(this IEnumerable<T> source, string separator = ",") => string.Join(separator, source);
<think>嗯,用户现在想了解如何在C#中编写扩展属性。我之前回答过关于FlaUI获取桌面分辨率缩放比的问题,现在这个问题转向C#语言特性,可能用户正在开发需要扩展已有类的项目,但不想或无法修改原始代码。 首先,我需要确认C#是否支持扩展属性。根据我的知识,C#允许扩展方法,但扩展属性并不是直接支持的。不过用户可能指的是如何模拟扩展属性的效果,或者是否有其他方法实现类似功能。这需要详细解释。 用户可能已经知道扩展方法,但可能对扩展属性不太清楚。他们可能希望像扩展方法一样,给现有类型添加属性。需要说明C#没有原生的扩展属性,但可以通过其他方式模拟,比如使用扩展方法结合一个静态类来存储属性值,或者使用条件WeakTable来管理实例和附加属性之间的关系。 接下来,我需要考虑用户的实际需求。他们可能想要向现有类型添加属性,比如给string类添加一个IsNumeric属性,或者给某个第三方库的类添加自定义属性。这时候,使用扩展方法配合静态字典可能是一个解决方案,但要提醒线程安全和内存管理的问题。 另外,可能需要提到C# 9或更高版本是否有新特性支持,比如init访问器或记录类型,但这些可能不直接相关。需要明确说明,即使在新版本中,扩展属性仍然不被直接支持,但可以通过模式实现类似效果。 用户可能遇到的问题包括如何管理附加属性的生命周期,避免内存泄漏,以及如何确保线程安全。使用ConditionalWeakTable可以解决部分问题,因为它能自动处理垃圾回收,但需要示例代码来说明。 此外,可能需要对比其他语言如Swift或Kotlin中的扩展属性,说明C#中的不同之处,避免用户混淆。同时,提醒用户扩展方法的使用场景,以及何时应该使用继承或装饰器模式代替扩展属性。 最后,确保回答结构清晰,分步骤说明实现方法,提供代码示例,并指出优缺点和注意事项,帮助用户全面理解如何在C#中模拟扩展属性。</think>--- ### **1. 核心概念** C#中的**扩展属性(Extension Properties)**并不是原生支持的语法,但可以通过**扩展方法 + 属性模式**模拟类似行为。其核心思路是: - 通过**扩展方法**为现有类型添加"伪属性"的访问接口 - 结合**静态类**和**条件WeakTable**管理附加属性的存储 --- ### **2. 基础实现方法** #### **示例:为`string`类型添加`IsNumeric`扩展属性** ```csharp public static class StringExtensions { // 使用扩展方法模拟属性getter public static bool IsNumeric(this string str) { return double.TryParse(str, out _); } // 如果要支持setter,可使用参数传递值 public static void SetIsNumeric(this string str, bool value) { // 示例逻辑(实际需要更复杂的存储机制) } } // 使用示例 string input = "123"; bool isNum = input.IsNumeric(); // 类似属性访问 ``` --- ### **3. 进阶:附加真实属性的实现** 当需要为对象附加**状态性属性**时,需使用存储容器: #### **方案一:使用`ConditionalWeakTable`(推荐)** ```csharp using System.Runtime.CompilerServices; public static class ObjectExtensions { // 存储附加属性值的容器 private static readonly ConditionalWeakTable<object, PropertyBag> _props = new(); // 扩展属性访问器 public static int CustomProperty(this object obj) { return _props.GetOrCreateValue(obj).CustomValue; } public static void SetCustomProperty(this object obj, int value) { _props.GetOrCreateValue(obj).CustomValue = value; } // 属性包类 private class PropertyBag { public int CustomValue { get; set; } } } // 使用示例 var myObj = new object(); myObj.SetCustomProperty(42); Console.WriteLine(myObj.CustomProperty()); // 输出42 ``` #### **关键优势** - **自动内存管理**:`ConditionalWeakTable`在宿主对象被GC回收时自动清理关联数据 - **线程安全**:内置同步机制 - **类型安全**:强类型存储 --- ### **4. 完整模式实现** #### **步骤1:定义泛型扩展容器** ```csharp public static class ExtensionProperties<T> where T : class { private static readonly ConditionalWeakTable<T, PropertyStore> _store = new(); private class PropertyStore { public Dictionary<string, object> Properties { get; } = new(); } public static TValue GetProperty<TValue>(this T instance, string propertyName) { var store = _store.GetOrCreateValue(instance); return store.Properties.TryGetValue(propertyName, out var value) ? (TValue)value : default; } public static void SetProperty<TValue>(this T instance, string propertyName, TValue value) { var store = _store.GetOrCreateValue(instance); store.Properties[propertyName] = value; } } ``` #### **步骤2:使用动态属性** ```csharp // 为任何对象添加自定义属性 var person = new Person(); person.SetProperty("Age", 30); int age = person.GetProperty<int>("Age"); ``` --- ### **5. 替代方案比较** | 方法 | 优点 | 缺点 | |-----------------------|-------------------------------|-------------------------------| | 扩展方法模拟属性 | 简单直观 | 无法存储状态 | | ConditionalWeakTable | 安全的状态管理 | 实现稍复杂 | | 动态对象(ExpandoObject)| 动态扩展 | 类型不安全,性能较低 | | AOP框架 | 强大的扩展能力 | 依赖第三方库,学习曲线陡峭 | --- ### **6. 实际应用场景** 1. **为密封类添加调试信息** ```csharp public static class DebugExtensions { public static string GetDebugInfo(this Exception ex) { return $"【异常追踪】{ex.GetType().Name}: {ex.Message}"; } } ``` 2. **为UI控件附加元数据** ```csharp // 为所有控件添加Tag扩展属性 button.SetProperty("CustomTag", "重要按钮"); ``` 3. **跨程序集扩展第三方类型** ```csharp // 扩展ASP.NET Core的HttpContext public static class HttpContextExtensions { public static string GetClientFingerprint(this HttpContext context) { return context.Request.Headers["User-Agent"] + context.Connection.RemoteIpAddress; } } ``` --- ### **7. 重要注意事项** 1. **性能考量**:频繁访问ConditionalWeakTable可能影响性能,建议缓存常用属性 2. **命名冲突**:使用唯一属性名,建议添加命名空间前缀 3. **线程安全**:若使用自定义字典存储,需实现同步机制 4. **类型转换**:动态类型需处理类型转换异常 5. **设计原则**:优先考虑继承或组合模式,扩展属性应作为最后手段 --- ### **8. C# 10+新特性支持** 虽然C# 10引入了**扩展属性模式**的改进,但仍是基于方法调用的语法糖: ```csharp // 使用新语法模拟属性 public static class CollectionExtensions { public static int Count<T>(this IEnumerable<T> source) => source.Count(); // 使用索引器扩展 public static T First<T>(this IEnumerable<T> source) => source.First(); } ``` --- ### **9. 最佳实践建议** 1. **明确的命名规范**:`GetXxx()/SetXxx()`保持方法命名清晰 2. **单元测试覆盖**:特别关注类型转换和空值处理 3. **文档注释**:明确扩展属性的用途和限制 4. **性能分析**:在高频调用场景进行基准测试 5. **依赖管理**:避免在公共库中过度使用扩展属性 --- 通过上述方法,可以在C#中有效实现类似扩展属性的功能,增强代码的可扩展性和可维护性。实际开发中应根据具体需求选择最合适的实现方案。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值