我不确定您的问题是仅在 LINQ 的 Where 中,还是在 where T: DtoBase 中,所以我将两者都讨论
在泛型方法中的位置
public static void Merge<T>(ObservableCollection<T> target,
ObservableCollection<T> source)
where T : DtoBase
方法Merget<T>的标识符后面的<T>,告诉我们这是一个generic method,也就是说你可以用各种类调用它,只要类满足where条件:
where T : DtoBase
所以你可以用任何类 T 调用这个方法,只要这个类是(派生自)DtoBase。
例子:
class DtoBase {...};
class Derived : DtoBase {...};
ObservableCollection<DtoBase> myBases = ...
ObservableCollection<DtoBase> yourBases = ...
ObservableCollection<Derived> myDeriveds = ...
ObservableCollection<Derived> yourDeriveds = ...
现在您可以调用方法Merge,并将T 之外的所有出现替换为与where T : DtoBase 匹配的任何内容。所以以下都是有效的:
Merge(myBases, yourBases);
Merge(myBases, myBases);
Merge(myDeriveds, yourDeriveds);
Merge(myBases, myDeriveds);
以下内容无法编译:
Merge(myBases, 4); // integer 4 is not a DtoBase
Merge("Hello", myBases); // string is not a DtoBase
在 LINQ 语句中的位置
var targetHandles = target.Where(t => true);
该语句中的t与泛型方法中的T无关。
如果您查看Enumerable.Where 的定义,您会发现它是一个通用扩展方法。
public static IEnumerable<TSource> Where<TSource> (
this IEnumerable<TSource> source,
Func<TSource,bool> predicate);
上面讨论了泛型:Where 有一个类型 TSource,与我们之前讨论的 T 相当:无论您在哪里看到 TSource,都应该将其替换为相同的实际类型。
我们还在第一个参数前面看到了关键字this。这意味着它是一个扩展方法:你可以把第一个参数放在方法的前面,像这样:
IEnumerable<Product> products = ...
IEnumerable<Product> cheapProducts = products.Where(product => product.Price < 1);
这与:
IEnumerable<Product> cheapProducts = Enumerable<Product>.Where(products,
product => product.Price < 1);
所以扩展方法只是一些花哨的语法糖。它使它看起来好像是 Product 的一种方法。
现在是有趣的部分:product => product.Price < 1 中的 product。 product 只是一个名称选择得当的标识符。我也可以写:
.Where(t => t.Price < 1);
t => t.Price < 1 称为 lambda 表达式。它是函数定义的一些简写:我们创建一个带有输入参数t 的方法,它返回t.price < 1。 t 的类型可以在泛型中找到:
public static IEnumerable<TSource> Where<TSource> (
this IEnumerable<TSource> source,
Func<TSource,bool> predicate);
在我的示例中,TSource 是 Product,因此第一个参数应该是 IEnumerable<Product>。返回值也是IEnumerable<TSource>。
IEnumerable<Product> products = ...
IEnumerable<Product> cheapProducts = products.Where(...);
predicate 是Func<TSource, bool>。这意味着:任何带有输入 TSource(在我们的例子中是 Product)的函数都返回一个布尔值。所以如果你在某个地方看到:
Func<int, string, DateTime, Point>
那么它的意思是:任何方法,具有int、string、DateTime三个类型的输入参数(按此顺序),它返回一个Point:
Point MyFunct(int i, string txt, DateTime date) {...}
我们已经看到 lambda 表达式t => t.Price < 1 是一个将Product t 作为输入参数并返回t.Price < 1 的值的方法,这显然是一个布尔值。因此,这个 lambda 表达式匹配 Func。
IEnumerable<Product> cheapProducts = products.Where(product => product.Price < 1);
那么这有什么作用呢?它从products 的序列中取出每个product,并将其放入函数product => product.Price < 1。换句话说:从 Products 序列中的每个产品,它计算布尔值product.Price < 1。是真的,那么我们就保留它,如果不是,我们就不按照Where返回的顺序使用它。
效果是,Where(product => product.Price < 1) 返回所有 Products 的序列,其属性 Price 的值小于 1。
回到你的问题
您的Where 是一个相当奇怪的人。
IEnumerable<Target> targets = ...
targets.Where(t => true);
我们知道targets 是一个Target 类型的序列。因此,我们知道谓词是一种将 Target 作为输入并返回 bool 的方法:
布尔谓词(目标t){返回真}
嗯,它确实返回一个布尔值。事实上,它总是返回相同的布尔值,它甚至不看输入参数 t,它总是返回 true。
Where(t => true) 类似于以下内容:
foreach(Target target in targets)
{
if (true) return target;
}
好吧,这没什么用:它返回完整的输入序列:
IEnumerable<Target> targets = ...
var result = targets.Where(t => true);
我们知道result 与targets 具有相同的元素,顺序相同。该语句不是很有用。
结束语
当您必须在 LINQ 语句中定义自己的标识符时,请尝试使用复数名词来标识序列,使用单数名词来标识序列的元素。尽量避免使用无意义的标识符 t =>。
输入更少的字符并不是选择错误标识符的好借口!
IEnumerable<Customer> customers = ...
IEnumerable<Order> orders = ...
var customersWithTheirOrders = customer.GroupJoin(orders,
customer => customer.Id, // from every Customer take the primary key
order => order.CustomerId, // from every Order take the foreign key
(customer, ordersOfThisCustomer) => new
{
Id = customer.Id,
Name = customer.Name,
Address = customer.Address,
Orders = ordersOfThisCustomer.Select(order => new
{
Date = order.Date,
Total = order.Total,
})
.ToList(),
})
这是一个非常大的 LINQ。 GroupJoin你可能不熟悉,但是因为我用了复数和单数名词,所以读起来并不难:
简而言之:我们有一系列客户和一系列订单。我们集团加入客户和订单。这意味着,我们从每个客户那里获取 ID,从每个订单中获取 CustomerId。我们试图匹配它们。从每个有零个或多个订单的客户中,我们制作一个新对象。
这个新对象包含客户的 ID,以及他的姓名和地址。从该客户的订单序列中的每个订单中,我们创建一个新对象。这个新对象包含订单的日期和总计。
因为我使用了复数和单数名词,所以这个文字描述非常符合LINQ。您不必想知道t 和x 是什么。 Customer 和 OrdersOfThisCustomer 更容易理解它们的含义。