使用泛型类型参数代替System.type类型的参数.这是一种气味吗

本文关键字:一种 参数 泛型类型参数 System 类型 type | 更新日期: 2023-09-27 18:27:54

我经常看到(例如,在许多mocking库中)使用泛型类型参数来代替System.Type类型的参数的方法。我特别讨论的是泛型类型仅在typeof(T)操作中使用的情况(即,在方法中的任何位置都没有使用类型T的实例,并且T既没有用于返回类型也没有用于其他参数)。

例如,考虑以下方法:

public string GetTypeName(System.Type type) { return type.FullName; }

这种方法通常伴随着一个通用版本:

public string GetTypeName<T>() { return GetTypeName(typeof(T)); }

问题这是一种坏的做法还是一种好的做法
这是句法上的糖吗?还是还有更多的糖?

我认为这是滥用语言功能来缩写对接受System.Type 类型参数的方法的调用

你会认为这是一种气味吗?应该避免这种情况吗?或者这实际上是一种好的实践(提供一种通用方法作为避免键入typeof()的快捷方式)。

以下是我能想到的使用这种模式的一些实际问题:

  1. 如果添加了非System.Type类型的参数,则方法可能需要重写为非泛型版本(如果参数的顺序在语义上重要)(否则,一些参数将是泛型类型参数,而一些将是常规参数)
  2. 它需要两种方法(泛型和非泛型,用于编译时类型未知的情况)。因此增加了单元测试,这些测试大多毫无意义

另一方面,这是一种常见的做法(大多数人总是对的,对吧。

使用泛型类型参数代替System.type类型的参数.这是一种气味吗

我认为您需要考虑文档。这些方法的作用有多明显?如果有两个方法(一个带有Type,另一个带有类型参数),则用户需要同时查看这两个方法并进行选择。没有看过你的代码的人可能没有意识到第二个代码只是调用了第一个代码。

同时使用这两种方法确实有意义的地方是,当类型参数可以实际使用时,以及Type版本有某种回退。例如:

object GetThingOfType(Type type) { ... }
T GetThingOfType<T>() { return (T)GetThingOfType(typeof(T)); }

需要考虑的另一件事是:类型参数必须始终显式编写。如果同一类型对象可能要执行多个操作,那么使用类型参数是没有帮助的。考虑一下这样的东西:

var t = typeof(string);
var name = GetTypeName(t);
var assemblyName = t.Assembly.FullName;

尽管我知道类型是string,但我不应该在这里写GetTypeName<string>,因为我会重复自己。通过给我一个我通常最好不要选择的选项,你增加了一点不必要的复杂性。

更模糊的一点是IDE对XML文档的支持。你这样记录类型参数:

<typeparam name="T">important information</typeparam>

然后,如果在C#中键入GetTypeName<,Visual Studio将显示"T:重要信息"。但是,由于某些原因,当您在Visual Basic中键入GetTypeName(Of时,它不会(截至2012年)。

您是对的:考虑方法的语义。它是在类型的实例上操作,还是在类型本身上操作?

如果它是在实例上操作的,那么它应该是一个泛型方法。如果它在类型上,则使其成为Type类型的参数。

所以在你的例子中,我会说

public string GetTypeName(System.Type type) { return type.FullName; }

public static int Count<TSource>(this IEnumerable<TSource> source)

正在类型为IEnumerable<TSource>的实例source上操作。

总的来说,我看到泛型被滥用而不是被充分使用。任何类型为(T)或更糟的泛型方法实现,使用任何类型的反射,在我看来都不是真正的泛型,这是一种滥用。毕竟,泛型意味着无论类型参数如何,它都能正常工作,不是吗?

总之,我同意你的看法——它闻起来很香。

我不使用string GetName<T>() { return typeof(T).Name; }模式,因为它是设计模式的误用(误用可能很严重,但我想不出正确的词),这是泛型的原因,即:编译器和JITter都有泛型类型参数(见这个问题的答案),这样它们就可以生成特定类型的存储、参数、堆栈变量,等等

使用它作为一种在运行时向我传递类型参数的方便方法很有味道。有时typeof(T)是必要的,但我发现它们很少见,通常只有在用泛型做复杂的事情时才需要,而不是简单的类型安全类型的事情。如果我看到它,我一定会停下来问自己为什么它在那里。

正如前面所说,这在我看来是个人偏好的问题。根据我的经验,大多数接受type类型参数的方法都可以使用附带的扩展方法在语法上进行"优化"。

因此,在您的例子中,我将把第二个方法作为扩展。使用这种方法,您可以获得第二个问题的解决方案——不需要不必要的单元测试。然而,第一个仍然存在;但是,添加参数无论如何都需要重构,因此它将提供一个机会来更改扩展方法的使用者,使他们使用原始方法的修改版本。

当然,这只是个人意见。

与参数类型为Type的方法相比,泛型方法具有优势,因为它可以使用所提供的类型引用其他泛型。这在以下情况下特别有用:

public string GetTypeName<T>()
{ 
    return Cache<T>.TypeName;
}
private static class Cache<T>
{
    public static readonly TypeName = GetTypeName(typeof(T));
}

这个缓存很简单,不需要摆弄字典,而且它是自动线程安全的。

话虽如此,如果实现没有使用这种可能性,那么两者之间的区别只是表面上的。

处理类型的方法通常只做这件事:处理类型。

IMO,Class.Method<SomeType>();Class.Method(typeof(SomeType)); 好得多

但我想这是一个意见问题。

考虑LINQ的.OfType<T>(),例如:

personlist.OfType<Employee>().Where(x => x.EmployeeStatus == "Active");

对比:

personlist.OfType(typeof(Employee)).Where(x => ((Employee)x).EmployeeStatus == "Active");

你更喜欢哪一个?

相关文章: