【问题标题】:Deducing if a generic is an array type at compile time在编译时推断泛型是否为数组类型
【发布时间】:2021-09-08 06:13:13
【问题描述】:

正如上游记录的那样,不能使用“数组”作为基本约束:

[...]Some types are disallowed as a base class constraint: Object, Array, and ValueType.[...]

然而,数组的类型推断似乎很少,如下所示:

我的目标是扩展原来的 SO 帖子,同时处理数组类型:

所以我的幼稚尝试如下:

public class JsonDictionary
{
    public static readonly Key<int> Int = new Key<int> { Name = "INT" };
    public static readonly Key<int[]> Int3 = new Key<int[]> { Name = "INT3" };
    
    IDictionary<string, object> _data;
    public JsonDictionary()
    {
        _data = new Dictionary<string, object>();
    }
    public void Set<T>(Key<T> key, T obj)
    {
        if (obj is int[] objArray) // FIXME: runtime check
        {
            if (objArray.Length != 3)
                throw new FormatException("Illegal INT3");
        }
        _data[key.Name] = obj;
    }
    public T Get<T>(Key<T> key)
    {
        return (T)_data[key.Name];
    }
    public class Key<T>
    {
        public string Name { get; init; }
    }
}

用法很简单:

var d = new JsonDictionary();
d.Set(JsonDictionary.Int, 42);
var i = d.Get(JsonDictionary.Int);
d.Set(JsonDictionary.Int3, new int[] { 1, 2, 3 });
var i3 = d.Get(JsonDictionary.Int3);
Assert.Throws<FormatException>(() => d.Set(JsonDictionary.Int3, new int[] { 1, 2 }));

请注意我如何使用运行时检查 (obj is int[] objArray),因为我无法对“数组”使用某种约束。

有没有办法重写上面的,这样我就可以有两个不同的Set 函数,一个用于T,一个用于T[]。否则我需要用丑陋的 if/else 来处理所有可能的类型:

        if (obj is int[] objArray)
        {
            if (objArray.Length != 3)
                throw new FormatException("Illegal INT3");
        }
        else if (obj is ushort[] objArray)
        {
            if (objArray.Length != 3)
                throw new FormatException("Illegal USHORT3");
        }
        else if (obj is double[] objArray)
        {
            if (objArray.Length != 3)
                throw new FormatException("Illegal DOUBLE3");
        }
        [...]

【问题讨论】:

  • 澄清一下:您是在问如何创建一个对输入作为数组进行约束的泛型方法,还是在问如何确定一个变量是否包含一个数组?
  • 考虑创建自定义类型来表示约束的替代方法。 使非法代表成为不可能
  • @Llama 我同意,但是由于 OP 要求 generic 函数,所以 string 也应该是有效的。否则他一开始就不应该使用泛型。
  • "有没有办法重写上面的代码,这样我就可以拥有两个不同的Set 函数,一个用于T,一个用于T[]" 我很确定重载分辨率会选择如果您传递数组类型,则数组重载,因此您应该能够编写另一个重载,例如public void Set&lt;T&gt;(Key&lt;T[]&gt; key, T[] obj)。 (this is also the answer to one of your linked posts) 是什么阻止你这样做?
  • 请添加一些关于约束应该实际匹配的类型的信息。例如string[] 呢? List&lt;int&gt; 也可以吗?还是DateTime[]?问问自己这些类型有什么共同点。

标签: c# arrays


【解决方案1】:

您可以检查它是否是Array

if (obj is Array arr && arr.Length != 3)
{
    throw new FormatException("Illegal array. Expected length 3.");
}

【讨论】:

  • 这与 OP 已经拥有的几乎相同,不是吗?
  • @HimBromBeere 是的,但无需他们显式地实现所有可能的数组类型。
  • 但它与您在 cmets 中提到的问题相同 - 例如对于字符串。
  • @HimBromBeere 这不是我在测试中看到的。 string 不是从 Array 派生的:example
  • new string[3] 怎么样。我不清楚 igf 是否是 OP 的有效数组。
【解决方案2】:

看来您根本不应该在这里使用泛型。 int[]double[] 之间绝对没有任何共同点,因为对数字没有限制 - 对数组也没有限制。

可以通过IEnumerable的约束以某种方式解决您的问题,但这也适合int[],但也适合List&lt;MyClass&gt;

所以即使如果有数组的约束,也没有数组的约束-类型

所以毕竟你应该为不同的类型有不同的方法:

Set(int i) => ...
Set(int[] arr) => ...
Set(double d) => ...
Set(double[]) => ...

【讨论】:

  • 假设 OP 永远不会传递 object 并期望该方法“处理它”,那么总体而言,使用不同的方法似乎是更好的选择。
  • 我无法理解第一句话It seems like you shouldn't use generics here at all. 您能否提供一个关于如何完成上述操作的最小代码示例?
  • @malat 每种类型只有一种方法:一种用于int,一种用于int[],一种用于double,一种用于double[],依此类推。
  • 好的,谢谢你的澄清,现在我明白了。无论如何,以上内容基于之前的帖子:stackoverflow.com/a/2178769/136285。我仍然相信您的 Set 有点重复,我认为 Generic 会很合适。
  • @malat 以减少重复,您可能有一个私有方法来执行所有常见逻辑。
猜你喜欢
  • 1970-01-01
  • 2018-07-28
  • 1970-01-01
  • 1970-01-01
  • 2021-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多