如何使用方法写入通用数组?指定类型后,通用数组不会更改数据类型

Posted

技术标签:

【中文标题】如何使用方法写入通用数组?指定类型后,通用数组不会更改数据类型【英文标题】:How to write into a generic array using a method? Generic array doesn't change datatype after specifying the type 【发布时间】:2021-11-03 17:02:34 【问题描述】:

如何使用 writeArray 方法将值写入通用数组 T[]?编译器报错,见代码中的注释。

似乎泛型数组仍然像 T 一样泛型。 我想通过指定 T 是 int 来初始化 intObj,对象中的每个占位符 T 现在都是数据类型 int?因此,对于发生错误的 this.array[i],我希望 T[] 变为 int[]。提前谢谢你。


class GenericArray<T> 
    Random rnd=new Random();
    T[] array = new T[3];
    
    public void writeArray() 
        for (int i = 0; i<this.array.Length; i++) 
            this.array[i]=rnd.Next(1,10);   //Cannot implicitly convert type 'int' to 'T'
        
    


class Program 
    public static void Main() 
        GenericArray<int> intObj = new GenericArray<int>();
        
        intObj.writeArray();
        

【问题讨论】:

如果我做了new GenericArray&lt;string&gt;().writeArray(); 会怎样?听起来你根本不需要泛型。 这对我来说只是泛型工作原理的练习。使用 GenericArray 产生相同的错误(无法将随机 int 数转换为 T) 我请你想想如果我这样做new GenericArray&lt;string&gt;().writeArray();会发生什么。它应该是一种教学设备,而不应该修复您的代码。请记住,T 作为泛型类型参数可以是任何东西,那么在 GenericArray&lt;string&gt; 对象上调用 writeArray 是否有意义?现在你明白为什么你的代码不能编译了吗? 对不起,我一定是错过了一些重要的知识,因为我真的什么都想不起来。也许使用GenericArray&lt;string&gt; 方法writeArray 也不能将随机数从int 转换为字符串?而且编译器只是没有说“不能从int转换为字符串”而是“int to T”? 没错。 T 不一定是 int,对吧?如果它恰好是string,那么rnd.Next 返回的整数就不再是正确的类型了!编译器必须能够证明您的代码在所有情况下都是类型安全的。这就是它尝试将int 转换为T 的原因。您可以放入数组的唯一类型是T - 所有其他类型在某些 情况下都会失败。如果你输入了int,那么如果T 变成string 就行不通了。 【参考方案1】:

泛型于T 的类必须能够处理所有类型的T。这意味着无论指定什么 TwriteArray 都必须工作。当然,如果T 不是数字数据类型,您的版本将无法工作,因为Random 总是生成数字。因此,这是不允许的。

我们需要做的是重写你的类,使它只包含对所有T 通用的代码。

abstract class GenericArray<T>

    protected T[] array = new T[3];

    protected abstract T GenerateRandomValue();

    public void WriteArray()
    
        for (int i=0; i<this.array.Length; i++)
        
            this.array[i] = GenerateRandomValue();
        
    

这是有道理的,因为我们知道如何存储任何T 的数组(我们声明它),并且我们知道如何循环任何T 的数组(我们使用for),但我们不知道'不知道如何为任何T 生成随机值(因此我们将其保留为abstract)。

当您需要包含特定于类型的逻辑时,您可以关闭类型(从它继承并声明类型参数)并在那里提供实现。

class IntegerArray : GenericArray<int>

    Random rnd = new Random();

    protected override int GenerateRandomValue() => rnd.Next(1,10);

当然,您也可以对其他类型执行此操作。这是一个字符串。由于Random 产生数字,我们需要使用不同的函数来获取随机字符串。在此示例中,我只使用 GUID。

class StringArray : GenericArray<string>

    protected override string GenerateRandomValue() => System.Guid.NewGuid.ToString();

为了强调重点,这里还有一个使用复杂类型的示例 T

class PointArray : GenericArray<Point>

    Random rnd = new Random();
    protected override Point GenerateRandomValue() => new Point(rnd.Next(0,100), rnd.Next(0,100));

现在您可以在主程序中执行此操作:

var array = new IntegerArray();
array.WriteArray();

【讨论】:

我一定是做错了什么。 protected override T GenerateRandomValue() =&gt; rnd.Next(1,10); 给了我错误 type or namespace name 'T' could not be found'IntegerArray.GenerateRandomValue()': return type must be 'int' to match overridden member 'GenericArray&lt;int&gt;.GenerateRandomValue()'. 由于 IntergerArray 继承自 GenericArray,T 应该被转换为 int,不是吗?因此返回类型T不再存在并且不能被GenerateRandomValue()使用 对不起,我是AFK,没有尝试编译,所以肯定会有一些错误。我刚刚做了一些更改,请再试一次。 是的,现在可以了。非常感谢,我一定会好好研究的。【参考方案2】:

这里

this.array[i]=rnd.Next(1,10);

rand.Next() 返回一个整数;无论您在自己的调用Next() 的类中编写什么,它总是返回一个int。左侧操作数的类型始终为T。例如,如果您传递了 string(如 cmets 中所述)或 FileStream,编译器将如何知道该怎么做?将int 分配为FileStream 时,它会如何猜测您期望得到什么样的结果?

可以做的是为您的泛型类提供一种从 int 获取 T 类型对象的方法,无论是在创建时还是在 WriteArray() 方法中:

class GenericArray<T> 

    Random rnd = new Random();
    T[] array = new T[3];
    
    public void WriteArray(Func<int, T> FuncToGetTFromInt) 
    
        for (int i = 0; i<this.array.Length; i++) 
        
            this.array[i] = FuncToGetTFromInt(rnd.Next(1,10));   
        
    

你会这样使用它:

Func<int, string> FuncToGetStringFromInt = (value) => 

    return $"This int's value was value.ToString()";
; 
intObj.writeArray(FuncToGetStringFromInt);

或者(最好)你会提供提供 T 类型随机对象的整个函数:

public void writeRandomArray(Func<T> FuncToGetRandomT) 

    for (int i = 0; i<this.array.Length; i++) 
    
        this.array[i] = FuncToGetRandomT();   
    

然后像这样使用它:

Random rnd = new Random();

GenericArray<string> aStr = new GenericArray<string>();
Func<string> FuncToGetRandomString = () => return $"Hello world rnd.Next()!";;  
aStr.WriteRandomArray(FuncToGetRandomString);

GenericArray<int> aInt = new GenericArray<int>();
Func<int> FuncToGetRandomInt = () => return rnd.Next();;
aInt.WriteRandomArray(FuncToGetRandomInt);

【讨论】:

以上是关于如何使用方法写入通用数组?指定类型后,通用数组不会更改数据类型的主要内容,如果未能解决你的问题,请参考以下文章

Swift如何实现通用类型的弱引用数组(下)

Spark - 将通用数组传递给 GenericRowWithSchema

Swift如何实现通用类型的弱引用数组(下)

如何创建通用数组? [复制]

怎么用new定义一个类数组,并且释放内存

[C++] 通用类型数组