泛型基本用法

6

我在我的类中一直使用下面的函数,希望将其编写为泛型。

public static IEnumerable<MyObject> Get(string csvFile)
{
    return csvFile
        .ReadAsStream()
        .SplitCrLf()
        .Where(row => !string.IsNullOrWhiteSpace(row))
        .Select(row => new MyObject(row.Split(',')));
}

我尝试了下面的代码,但没有起作用

public static IEnumerable<T> Get<T>(string csvFile)
{
    return csvFile
        .ReadAsStream()
        .SplitCrLf()
        .Where(row => !string.IsNullOrWhiteSpace(row))
        .Select(row => new typeof(T)(row.Split(',')));
}

请提供建议。谢谢!


3
没起作用,是以什么方式? - JLRishe
1
可能没有编译成功,因为 new typeof(T) 是无效的语法。 - cdhowie
你可以从csv文件生成一个IEnumerable<string>。要将其转换为IEnumerable<T>,您需要定义一个转换函数Func<string,T>并应用它。 - John Alexiou
那么每一行是一个MyObject还是每个元素都是一个MyObject - John Alexiou
1个回答

11

您不能使用new以这种方式创建泛型类型的实例1。考虑为函数提供一个工厂委托:

public static IEnumerable<T> Get<T>(string csvFile, Func<string[], T> factory)
{
    return csvFile
        .ReadAsStream()
        .SplitCrLf()
        .Where(row => !string.IsNullOrWhiteSpace(row))
        .Select(row => factory(row.Split(',')));
}

那么你可以这样调用它:
var myObjects = Get("file.csv", row => new MyObject(row));

或者,您可以返回一个 IEnumerable<string[]>2,让调用者决定如何处理它:

public static IEnumerable<string[]> Get(string csvFile)
{
    return csvFile
        .ReadAsStream()
        .SplitCrLf()
        .Where(row => !string.IsNullOrWhiteSpace(row))
        .Select(row => row.Split(','));
}

然后调用者可以执行:

var myObjects = Get("file.csv").Select(row => new MyObject(row));

1您可以使用where T : new()约束,然后可以使用泛型类型创建新实例,但仅当它提供无参数构造函数时才能这样做;在构造泛型类型时不能提供参数,而您的用例似乎需要。在这里,工厂委托是最佳选择。

作为参考,在无参数情况下使用泛型类型进行构造的方式如下:

public static T Create<T>() where T : new()
{
    return new T();
}

2更好的做法是使用一个 IEnumerable<IEnumerable<string>>,假设您的 MyObject 构造函数也接受 IEnumerable<string>


虽然有Activator.CreateInstance的方法,不过你提供的建议也很好,点赞(+1)。 - default
2
@默认情况下确实存在这种情况。事实上,new T()被编译为Activator.CreateInstance<T>()。然而,应该注意到这种技术(使用CreateInstance将参数传递给构造函数)每次都会使用反射,可能会产生严重的性能损失。使用工厂委托不仅提供了更多的灵活性(也许你想做的不仅仅是构造一个对象),同时还利用了编译代码并避免了反射的成本。 - cdhowie
太好了!我不知道那个。 - default
default(T) 也会编译成 Activator 吗? - default
1
@Default No; default(T) 的值仅在运行时实例化构造的泛型类型时确定一次。此时它将被替换为一个常量值。例如,如果 Tint,则 default(T) 的 IL 表示形式将是 ldc.i4.0(一个字面上的 0 常量)在构造的类型中。对于所有引用类型,它将是 ldnull(一个字面上的 null 常量)。对于结构体,它将是 new StructType(),这实际上只是将一块内存清零。简而言之:使用 default(T) 不会产生反射开销。 - cdhowie
@Default 如果你想进入更低的层次,default(T) 实际上意味着“一个大小为T的内存块,其中所有位都是零。” :) - cdhowie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接