为什么我应该在方法<类型>() 中指定<类型>?以及如何使用它
本文关键字:类型 何使用 我应该 方法 为什么 | 更新日期: 2023-09-27 17:56:51
考虑到下面的应用程序代码,有 3 个重载的扩展方法做同样的事情,并且具有相同的输入和输出:
- 使用调用和定义方法的参数有什么好处(或区别)?
- 为什么要使用它们(如果将调用没有它们的相同方法 3 并执行相同的操作)?
- 或者如何在方法中手动/显式使用它们(此类信息)?
法典:
namespace LINQ_CreatingYourFirstExtensionMethod
{
internal class Program
{
private static void Main(string[] args)
{
string[] aBunchOfWords = {"One", "Two", "Hello", "World", "Four", "Five"};
var iEnuResult = from s in aBunchOfWords
where s.Length == 5
select s;
iEnuResult.Write();
iEnuResult.Write<string>();
iEnuResult.Write<object>();//same effect as above line
Console.ReadKey();
}
}
}
重载的扩展方法 Write():
namespace LINQ_CreatingYourFirstExtensionMethod
{
public static class Utils
{
public static void Write(this IEnumerable<object> source) //****1
//public static void Write(this IEnumerable<string> source)//same effects as above line
{
foreach (var item in source)
{
Console.WriteLine(item);
}
}
public static void Write<T>(this IEnumerable<T> source)//****2
{
foreach (var item in source)
{
Console.WriteLine(item);
}
}
public static void Write(this IEnumerable source)//******3
{
foreach (var item in source)
{
Console.WriteLine(item);
}
}
}
}
更新:
你能给我任何最简单的例子为什么我需要
- 使用
<Type>
调用方法 - 使用
<Type>
定义方法
如果我可以在没有它的情况下做同样的事情?
更新2:
我没有立即写它,因为我认为从问题的上下文中很明显。
在当前代码中:
iEnuResult.Write();
电话
输入第一种方法:
公共静态空 写入(此 IE无数源)//**1- iEnuResult.Write();(或 iEnuResult.Write();)叫
进入第二种冰毒:公共静态空 Write1(此 IEnumerable 源)//**2
如果要注释掉
public static void Write1<T>(this IEnumerable<T> source)//*****2
然后
iEnuResult.Write1<object>();
无法制作(仅iEnuResult.Write();
)
如果要同时注释第 1 种和第 2 种方法,则调用 3d 方法
由iEnuResult.Write();
因此,可以在调用(调用)中使用和不使用<Type>
调用"相同"方法,也可以具有带和不带<Type>
的方法定义。
有多种组合(我会说几十种)来做看似相同的事情,我看不出有什么理由。
所以,我想了解这种差异存在的目的是什么,如果有的话
重点回答您的两个问题:
- 为什么要使用
<Type>
调用方法 - 为什么要使用
<Type>
定义方法
先问第二个问题。让我改写一下:我什么时候定义泛型方法?
当泛型方法面向许多类型,但一次只针对一种类型时,您将定义该方法。有两个很大的优势:
-
它确保(编译时)类型安全:
IEnumerable<string>
实际上只包含字符串,而不是奇数整数或其他任何东西。 -
这是一种使用类型而不装箱和取消装箱的方法(即分别将值类型与
object
相互转换)。正如这里所说:装
箱和取消装箱本身会导致显著的性能损失,但它也会增加托管堆的压力,从而导致更多的垃圾回收,这对性能不利。
如果只有非类型化方法 (3),则在枚举非类型化IEnumerable
时,每个字符串都会装箱(IEnumerator.Current
返回 Object
),并再次取消装箱以调用特定于类型的 ToString()
方法。源列表可以包含任何内容(这在这里无关紧要,但通常如此)。
通用方法 (2) 跳过装箱和取消装箱,并且是类型安全的。
第一种方法强制将任何输入参数(显式或隐式)转换为IEnumerable<object>
。同样,将发生装箱和拆箱。但另一个令人讨厌的事情是,并不总是有隐式转换可用,这会迫使该方法的使用者在调用它之前显式执行转换。其效果可以通过将select s
更改为select s.Length
来说明。如果只有方法 1 可用,您将获得
实例参数:无法从
'System.Collections.Generic.IEnumerable<int>'
转换为'System.Collections.Generic.IEnumerable<object>'
我认为一般的经验法则是避免使用object
(或IEnumerable<object>
等)类型的方法参数,除非没有其他选择。
简而言之:您的第二种方法是这里的最佳选择,因为它可以执行任何类型的任务,但避免装箱和取消装箱。
现在你的第一个问题。何时需要在方法调用中指定泛型类型参数?我能想到三个原因(可能还有更多):
- 当没有类型为
T
的方法参数时,编译器可以通过该参数推断泛型类型。工厂方法通常属于此类别。像T Create<T>()
这样的方法必须通过以下方式调用,例如Create<Customer>()
.构造函数是另一个示例:如果没有指定的类型,甚至无法调用泛型构造函数。 - 当您想要强制使用特定方法重载时。当继承类型混合了泛型和非泛型方法重载时,可能会发生这种情况。(顺便说一下,这不是好设计的标志)。
- 间接地在编译时不知道类型,而仅在执行时知道。请参阅 C# 中的泛型,使用变量的类型作为参数。