未知TSource时如何从ICollection中截取指定数量元素?
解决ICollection无法使用LINQ Take的非泛型实现方案
我来帮你搞定这个问题!首先,你遇到的CS1061错误原因很明确:非泛型的ICollection接口并没有实现泛型IEnumerable<T>,而LINQ的Take方法是针对泛型IEnumerable<T>的扩展方法,所以直接调用肯定会报错。而且你要求不能用泛型,还要保持返回类型与输入的value参数一致,那咱们换个思路用反射来实现这个需求。
核心解决方案思路
我们需要:
- 识别输入的
ICollection实例的具体类型 - 通过反射创建同类型的新集合实例
- 遍历原集合的前
_maxLength个元素,添加到新集合中 - 额外处理数组(因为数组实现了
ICollection但不能直接调用Add元素)
修改后的完整代码
using System; using System.Collections; using System.ComponentModel.DataAnnotations; using System.Linq; namespace Dado.ComponentModel.DataMutations { [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public class ApplyMaxLengthStackAttribute : MutationAttribute { private int _maxLength = -1; public object Mutate(object value, int maxLength, IMutationContext context = null) { _maxLength = maxLength; return Mutate(value, context); } protected override object MutateValue(object value, IMutationContext context) { if (value != null) { // 处理字符串的逻辑保持不变 if (value is string valueAsString && (_maxLength > -1 || TryGetStringLengthAttributeValue(context, out _maxLength)) && valueAsString.Length > _maxLength) { value = valueAsString.Substring(0, _maxLength); } // 先处理数组(数组实现ICollection但需要特殊处理) else if (value is Array valueAsArray && (_maxLength > -1 || TryGetMaxLengthAttributeValue(context, out _maxLength)) && valueAsArray.Length > _maxLength) { var elementType = valueAsArray.GetType().GetElementType(); var newArray = Array.CreateInstance(elementType, _maxLength); Array.Copy(valueAsArray, newArray, _maxLength); value = newArray; } // 处理非泛型ICollection else if (value is ICollection valueAsCollection && (_maxLength > -1 || TryGetMaxLengthAttributeValue(context, out _maxLength)) && valueAsCollection.Count > _maxLength) { var collectionType = valueAsCollection.GetType(); ICollection newCollection = null; try { // 创建同类型的新集合实例 newCollection = Activator.CreateInstance(collectionType) as ICollection; } catch (MissingMethodException) { // 如果没有无参构造函数,可以考虑 fallback 到 ArrayList 或者其他集合类型 newCollection = new ArrayList(); } if (newCollection != null) { int count = 0; foreach (var item in valueAsCollection) { if (count >= _maxLength) break; try { newCollection.Add(item); } catch (NotSupportedException) { // 处理只读集合的情况,这里可以选择返回原集合或者抛出异常 break; } count++; } value = newCollection; } } } _maxLength = -1; return value; } private bool TryGetStringLengthAttributeValue(IMutationContext context, out int maxLength) { var attribute = context?.Attributes.OfType<StringLengthAttribute>().FirstOrDefault(); if (attribute != null) { maxLength = attribute.MaximumLength; return true; } return TryGetMaxLengthAttributeValue(context, out maxLength); } private bool TryGetMaxLengthAttributeValue(IMutationContext context, out int maxLength) { var attribute = context?.Attributes.OfType<MaxLengthAttribute>().FirstOrDefault(); if (attribute != null) { maxLength = attribute.Length; return true; } maxLength = -1; return false; } } }
关键细节说明
- 数组处理:数组实现了
ICollection但长度固定,不能用Add方法,所以单独用Array.CreateInstance创建新数组并复制元素。 - 反射创建集合:用
Activator.CreateInstance创建原集合类型的实例,如果遇到没有无参构造函数的集合类型,做了fallback到ArrayList的处理,你可以根据需求调整这部分逻辑。 - 只读集合兼容:添加了
try-catch捕获NotSupportedException,避免在只读集合上调用Add方法报错,你可以根据业务需求选择返回原集合或者抛出异常。
内容的提问来源于stack exchange,提问作者roydukkey
相关产品推荐
相关产品推荐

