【问题标题】:Modifying an IEnumerable<T> or ICollection<T> when passed to a class using generic types使用泛型类型传递给类时修改 IEnumerable<T> 或 ICollection<T>
【发布时间】:2021-01-04 07:47:28
【问题描述】:

在使用泛型类型时,我在修改 IEnumerable 值时遇到了一些麻烦。
下面是一段代码:

public class ParameterArray<TEnumerable, TType> : IParameterArray<TEnumerable, TType> 
    where TEnumerable : ICollection<TType>    // Note that I have tried with IEnumerable<TType> instead of ICollection<TType>, but did not really help
{
    public ParameterArray(TEnumerable value, TType minValue, TType maxValue)
    {
        MinValue = minValue;
        MaxValue = maxValue;
        Value = value;
     }

    private TEnumerable _value;
    public TEnumerable Value
    {
        get => _value;
        set
        {
           ICollection<TType> checkedValue;

           // Here I want to check the range before setting the value
           // Note that Value can be of type int[], float[], bool[], byte[]
           
           // I have tried many work around, but for some reasons, I am never able to update _value
           for (int idx = 0; idx < value.Count(); idx++)
           {
               // Backup value after range limiting
               checkedValue.Add(CheckRange(value.ElementAt(idx)));
            }

            // Would like to save these new values in _value, but not sure how to
            _value = checkedValue as TEnumerable;  // Or (TEnumerable)checkedValue;     // !!! Breaking here at runtime - Cannot convert from one type to the other
        }
    }

    public TType CheckRange(TType value)
    {
        TType retValue = value;

        if (value != null)
        {
            // Restrict range to Min/Max
            if (Comparer<TType>.Default.Compare(MaxValue, value) < 0)
                retValue = MaxValue;
            else if (Comparer<TType>.Default.Compare(MinValue, value) > 0)
                retValue = MinValue;
        }
        else
        {
            throw new ArgumentOutOfRangeException($"Value cannot be null: Range is {MinValue} to {MaxValue}");
        }

        return retValue;
    }
}

不知何故,我无法将我的 ICollection 备份到我的 _value(这是一个 TEnumerable)中。但是,考虑到我已经声明 TEnumerable 派生自 ICollection(即 TEnumerable : ICollection),我不太明白原因

我尝试了多种方法,但它们都导致无法将我的结果分配给 _value 的相同问题。

欢迎提出任何建议。
也许我需要的不是:where TEnumerable : ICollection

编辑和解决方案

正如@nvoigt 所建议的那样,我已经重新格式化了我的类,使其不接受 TEnumerable 作为输入。这种额外的抽象层产生了额外的复杂性,使得一切都难以处理。
这是最终的解决方案:

public class ParameterArray<TType> : ParameterBase, IParameterArray<TType> 
{
    public ParameterArray(IEnumerable<TType> value, TType minValue, TType maxValue)
    {
        MinValue = minValue;
        MaxValue = maxValue;
        Value = value;
     }

    private IEnumerable<TType> _value;
    public IEnumerable<TType> Value
    {
        get => _value;
        set
        {
           IList<TType> checkedValue = new List<TType>();
           
           for (int idx = 0; idx < value.Count(); idx++)
           {
               // Range limiting
               checkedValue.Add(CheckRange(value.ElementAt(idx)));
            }

            // Back up the updated value
            _value = checkedValue.ToArray();
        }
    }

    public TType CheckRange(TType value)
    {
        // Unchanged
        ...
    }
}

【问题讨论】:

  • ICollection&lt;TType&gt; ckeckedValue = default; ...没有意义...它是null(和ckeckedValue.Add(...)会导致NPE)...另外,定义“不工作"
  • 提示 ...只需将 _value 设为 List&lt;TType&gt; ...
  • 编辑后:您可以将 setter 缩短为 _value = new List&lt;TType&gt;(value.Select(CheckRange)); ...
  • @Selvin,没有 _value 应该是一个数组
  • 见...still working :)

标签: c# generics collections ienumerable


【解决方案1】:
ckeckedValue as TEnumerable

让我们看看。假设我将TEnumerable 设为int[] 并且ckeckedValueICollection&lt;int&gt;,那么您的编译器是正确的……您不能使用as 运算符从相同基本类型的ICollection 创建数组。不管是不是泛型,这都行不通。

我真的不能提出一个好的方法。如果是我,我会废弃通用的可枚举部分,将其设为List&lt;&gt;Array&lt;&gt; 并完成它。如果您有明确的限制要求该部分是通用的,您可能应该将它们与问题一起发布,以便我们可以根据这些限制提出不同的解决方案。

【讨论】:

  • 感谢您的意见。所以你不会将该类声明为 ParameterArray?
  • 不,我会将它声明为ParameterArray&lt;T&gt;,它在构造函数中采用IEnumerable&lt;T&gt; 和 min 和 max 并构建它自己的列表。我的意思是,在您第一次调用 add 之后,无论如何您都会制作副本,您也可以从一开始就这样做。
  • 这将允许我以这种方式实例化类?新 ParameterArray
  • 不,它会让你这样使用它:new ParameterArray(new[]{1,2,3}, 1, 9);
  • 它似乎确实有效。再次感谢您的建议。按照你的建议重新定义我的班级是要走的路。我已经在这些泛型类型上苦苦挣扎了很长一段时间,希望我之前有你的意见。你一口气解决了2个问题!非常感谢!
猜你喜欢
  • 1970-01-01
  • 2011-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-13
  • 2018-03-26
  • 1970-01-01
相关资源
最近更新 更多