通用方法参数类型无法识别?

时间:2015-06-26 18:14:17

标签: c# generics

这可能是由于我对通用方法的可怕理解,所以我提前道歉。

我正在尝试编写一个继承自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的表达式。”

4 个答案:

答案 0 :(得分:3)

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

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

这将强制编译器选择类型为IEnumerable<string>

的第二个参数的重载

答案 1 :(得分:3)

在你的函数中,你使用string[]作为泛型类型参数,但是你的函数对你传递的内容一无所知,除了它是一个对象(你没有类型限制)。所以你的代码必须能够处理你可能传递的任何东西。现在你可能希望它会使用string.Join的{​​{3}}重载:

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

因为string[]实现了IEnumerable<string>,但编译器无法做到这一点。它根本不知道有关T的任何内容。它只知道它是一个对象。所以它唯一可以使用的重载是this

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然后将结果连接在一起。如您所见,在ToString上致电string[]会返回System.String[]。这是object.ToString()的基本实现,它只返回类型名称。

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

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

您正在使用Join的{​​{3}}重叠,需要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以做一些合理的事情。

当然,你实际上并没有获得太大的收益,只是简单地勾勒出对IEnumerable的号召,但它可能更具可读性,至少会让你有一个地方可以当有人决定你应该使用不同的分隔符时改变。

注意,如果您不需要为自己的收藏集编入索引,那么使用IList优于ToList。例如,你可以通过linq传递一些东西而不必首先writer.WriteLineFromCollection(line);

您只需使用以下方式调用它:

T

如果编译器可以解决问题,您不需要明确指定{{1}}。

答案 2 :(得分:0)

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

如果你想支持索引,或者像Sergey所说的那样,添加一个约束。

答案 3 :(得分:0)

你的代码的approch几乎没有问题,因为唯一会被传递的类型是String,因为它滥用了Genrics ... 你希望将Extenstion方法编写为字符串类型的prehapes,输出将是CSV格式的字符串?