简体   繁体   English

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

[英]Generic Method Argument Type Not Recognized?

This is probably due to my terrible understanding of generic methods, so I apologize in advance. 这可能是由于我对通用方法的深刻理解,所以我事先表示歉意。

I am trying to write a csvWriter class that inherits from StreamWriter. 我正在尝试编写从StreamWriter继承的csvWriter类。 One method I want it to have is to take a collection like an array or list and use String.Join to change it into a comma separated string, and then to write it as usual. 我希望它具有的一种方法是采用数组或列表之类的集合并使用String.Join将其更改为逗号分隔的字符串,然后照常编写。 I thought it would be simple, and if I use method overloading it is, but I am used to python and the idea of generics feels better to me, so I was giving it a shot. 我以为这很简单,如果我使用方法重载的话很简单,但是我习惯了python,泛型的想法对我来说感觉更好,所以我给了它一个机会。

However, my attempt is not working. 但是,我的尝试没有用。 The method doesn't seem to know what type is being passed to it, or something? 该方法似乎不知道传递给它什么类型,或者什么?

Class: 类:

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);
        }
    }

Lines to call it: 调用它的行:

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");
            }

The file, instead of having the string full of comma-separated values, ends up with "System.String[]." 该文件以字符串“ System.String []”结尾,而不是用逗号分隔的值填充字符串。 I think that I am actually giving it the type of the argument but not the argument itself, but don't know why this is the case. 我认为实际上是在给它提供参数的类型,而不是参数本身,但是不知道为什么会这样。 The debugger from inside WriteLineFromCollection is giving me what feels like conflicting information: It says line is of type string[], but when I try line[0] it says "Cannot apply indexing with [] to an expression of type T." 来自WriteLineFromCollection内部的调试器给我的感觉是相互冲突的信息:它说line是string []类型的,但是当我尝试line [0]时它说“不能将[]的索引应用于T类型的表达式。”

This happens because compiler chooses String.Join overload with 2nd parameter params object[] value , so you passes string array as one param. 发生这种情况是因为编译器选择String.Join重载并带有第二个参数params object[] value ,因此您将字符串数组作为一个参数传递。 To resolve this you can add constraint to WriteLineFromCollection method: 要解决此问题,可以向WriteLineFromCollection方法添加约束:

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

This will force compiler to choose overload with 2nd parameter of type IEnumerable<string> 这将强制编译器选择类型为IEnumerable<string>第二个参数的重载

In you function you are using string[] as your generic type argument, but your function knows nothing about what you are passing in other than it's an object (you have no type restriction). 在您的函数中,您使用string[]作为通用类型参数,但是您的函数对传入的内容一无所知,除了它是一个对象(您没有类型限制)。 So you code has to be able to work with whatever you might pass. 因此,您的代码必须能够处理可能通过的所有操作。 Now you are, presumably hoping it will use this overload of string.Join : 现在,您大概希望它会使用string.Join 重载:

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

since string[] implements IEnumerable<string> , but the compiler can't do that. 由于string[]实现了IEnumerable<string> ,但是编译器无法做到这一点。 It doesn't know if anything about T at all. 它根本不知道关于T任何信息。 All it knows is that it's an object. 它所知道的只是它是一个对象。 So the only overload of it can use is this one : 所以它唯一可以使用的重载就是这个

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

Now because of the params keyword here, you don't have to pass an Object[] explictly, you can just pass a bunch of arguments and it'll treat it like an object array. 现在,由于这里使用了params关键字,您不必显式传递Object[] ,您只需传递一堆参数即可将其视为对象数组。 For example, you could do something like this (not to say it's a good idea, because it probably isn't): 例如,您可以执行以下操作(不要说这是个好主意,因为它可能不是):

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

So what happens when you pass in your string[] is basically equivalent to if you'd done this: 因此,当您传入string[]时发生的事情基本上等同于您这样做:

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

Now what that overload does is it will iterate through each item in the object array and call ToString on it and then join the results together. 现在,该重载所做的是它将遍历对象数组中的每个项目,并在其上调用ToString ,然后将结果结合在一起。 Calling ToString on a string[] will return System.String[] as you saw. 如您所见,在string[]上调用ToString将返回System.String[] That's the base implementation of object.ToString() , it just returns the type name. 那是object.ToString()的基本实现,它只返回类型名称。

Obviously, this isn't what you want. 显然,这不是您想要的。 Something like this should work: 这样的事情应该起作用:

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

You are using this overload of Join which takes an IEnumerable<T> and will call ToString() on each item in your collection. 您正在使用Join重载, 重载使用IEnumerable<T>并将对集合中的每个项目调用ToString()

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

So now, when you pass in a collection of T (that implements IEnumerable<T> , which string[] does), it will be treated as a collection and not implicitly wrapped in an object array like before. 因此,现在,当您传入T的集合(实现IEnumerable<T>string[]这样做)时,它将被视为一个集合,而不像以前那样隐式包装在对象数组中。 Now it's iterates through you collection of T and calls ToString , which in the case of an actual string just returns the string itself. 现在,它遍历T集合并调用ToString ,在实际string的情况下,它仅返回string本身。 Note if you passed something other than string as your type parameter, then you will get whatever their ToString does, which might be just the type name. 请注意,如果您传递了string以外的其他东西作为类型参数,那么您将获得其ToString所做的任何事情,这可能只是类型名称。 So you'll probably want to override ToString in your own classes to do something sensible if you intend it to be used here. 因此,如果您打算将ToString用于此处,则可能需要重写自己的类中的ToString以执行明智的操作。

Of course, you really aren't gaining much out of this versus just inlining a call to String.Join , but it might be a little more readable and would at least give you a single place to have to change when somebody decides you should use a different separator. 当然,与内联对String.Join的调用相比,您实际上并没有String.Join获得太多String.Join ,但是它可能更具可读性,并且当有人决定您应该使用时,至少会给您一个地方进行更改一个不同的分隔符。

Note, using IEnumerable is preferable to IList here if you don't need to index into your collection. 请注意,如果不需要索引到集合中,则在此处使用IEnumerable优于IList That way you could, for example, pass something from linq without having to ToList it first. 这样,例如,您可以从linq传递某些内容而不必先将其ToList

You can call it simply with: 您可以使用以下命令简单地调用它:

writer.WriteLineFromCollection(line);

You don't need to specify T explicitly if the compiler can figure it out. 如果编译器可以找出T则无需显式指定T

I think you should try changing from public void WriteLineFromCollection<T>(T line) to public void WriteLineFromCollection<IList<T>>(IList<T> line) 我认为您应该尝试从public void WriteLineFromCollection<T>(T line)更改为public void WriteLineFromCollection<IList<T>>(IList<T> line)

if you want to support indexing, or as Sergey mentioned, add a constraint. 如果要支持索引编制,或者如Sergey所述,请添加约束。

代码的处理几乎没有问题,因为将要传递的唯一类型是String,这使它成为对泛型的滥用。...可能您想编写一个Extension方法以字符串类型输出,输出将是CSV格式化的字符串?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM