无法识别泛型方法参数类型

本文关键字:参数 类型 泛型方法 识别 | 更新日期: 2023-09-27 17:59:53

这可能是由于我对泛型方法的理解很糟糕,所以我提前道歉。

我正在尝试编写一个继承自StreamWriter的csvWriter类。我希望它拥有的一种方法是获取数组或列表之类的集合,并使用 String.Join 将其更改为逗号分隔的字符串,然后照常编写。我认为这会很简单,如果我使用方法重载它就是这样,但是我已经习惯了python,泛型的想法对我来说感觉更好,所以我试了一下。

但是,我的尝试不起作用。该方法似乎不知道传递给它的是什么类型,或者其他什么?

类:

public class csvWriter : System.IO.StreamWriter
    {
        public csvWriter(string filename) : base(filename)
        {
        }
        public void WriteLineFromCollection<T>(T line)
        {
            string newLine = String.Join(",", line);
            base.WriteLine(newLine);
        }
    }

台词称呼它:

using (var reader = new csvReader(filename_in))
using (var writer = new csvWriter(filename_out))
            {
                while (!reader.EndOfStream)
                {
                    string[] line = reader.ReadLine();
                    writer.WriteLineFromCollection<string[]>(line);
                }
                Console.Out.Write("PAUSE");
            }

该文件不是包含充满逗号分隔值的字符串,而是以"System.String[]"结尾。我认为我实际上是在给它论点的类型,而不是论点本身,但不知道为什么会这样。WriteLineFromCollection 内部的调试器给了我感觉像是相互矛盾的信息:它说 line 是 string[] 类型,但是当我尝试 line[0] 时,它说"无法将 [] 的索引应用于 T 类型的表达式"。

无法识别泛型方法参数类型

在函数中,你使用 string[] 作为泛型类型参数,但你的函数除了它是一个对象之外,对你传递的内容一无所知(你没有类型限制(。因此,您的代码必须能够处理您可能传递的任何内容。现在你大概希望它会使用这个重载string.Join

public static string Join(
    string separator,
    IEnumerable<string> values
)

因为string[]实现了IEnumerable<string>,但编译器不能这样做。它根本不知道T有什么。它只知道它是一个物体。所以它唯一可以使用的重载是这个:

public static string Join(
    string separator,
    params Object[] values
)

现在,由于这里的 params 关键字,您不必显式传递Object[],您只需传递一堆参数,它会将其视为对象数组。例如,你可以做这样的事情(不是说这是一个好主意,因为它可能不是(:

string.Join(",","somestring",1,true,new object());

因此,当您传入string[]时会发生什么基本上等同于您这样做时的情况:

string.Join("," new object[] { line });

现在,重载的作用是循环访问对象数组中的每个项目并对其调用ToString,然后将结果连接在一起。在string[]上呼叫ToString将返回System.String[]如您所见。这是object.ToString()的基本实现,它只返回类型名称。

显然,这不是你想要的。这样的事情应该有效:

public void WriteLineFromCollection<T>(IEnumerable<T> line)
{
    string newLine = String.Join(",", line);
    base.WriteLine(newLine);
}

您正在使用这种Join重载,它需要IEnumerable<T>,并将对集合中的每个项目调用ToString()

public static string Join<T>(
    string separator,
    IEnumerable<T> values
)

所以现在,当你传入一个T集合(实现IEnumerable<T>string[]这样做(,它将被视为一个集合,而不是像以前那样隐式包装在对象数组中。现在它遍历T集合并调用ToString,在实际string的情况下,它只返回string本身。请注意,如果您传递了 string 以外的其他内容作为类型参数,那么您将获得他们的ToString所做的一切,这可能只是类型名称。因此,如果您打算在此处使用它,您可能希望在自己的类中重写ToString以执行一些明智的操作。

当然,与只是内联对String.Join的调用相比,您真的没有从中获得太多好处,但它可能更具可读性,并且至少会为您提供一个在有人决定您应该使用不同分隔符时必须更改的地方。

请注意,如果您不需要索引到集合中,则最好在此处IList使用 IEnumerable。例如,这样您就可以从 linq 传递某些内容,而不必先ToList它。

您可以简单地调用它:

writer.WriteLineFromCollection(line);
如果

编译器可以弄清楚,则无需显式指定T

发生这种情况是因为编译器选择了 String.Join 重载与第二个参数 params object[] value ,因此您将字符串数组作为一个参数传递。要解决此问题,您可以向WriteLineFromCollection方法添加约束:

public void WriteLineFromCollection<T>(T line) where T : IEnumerable<string>
{
    string newLine = String.Join(",", line);
    base.WriteLine(newLine);
}

这将强制编译器选择具有类型为 IEnumerable<string> 的第二个参数的重载

我认为

您应该尝试从public void WriteLineFromCollection<T>(T line)更改为public void WriteLineFromCollection<IList<T>>(IList<T> line)

如果要支持索引,或者如 Sergey 所述,请添加约束。

代码的 approch 几乎没有问题,因为唯一要传递的类型是 String 使它滥用泛型...您想将扩展方法编写为字符串类型,输出将是CSV格式的字符串吗?