I'm struggling to understand this lines of code but I don't grow out of it.
The instruction is the following
var targetHandles = target.Where(t => true);
that is inside a function with these signature
public static void Merge<T>(ObservableCollection<T> target, ObservableCollection<T> source) where T : DtoBase
Thanks in advance for your answers
Ric
CodePudding user response:
I'm not sure whether your problem is only in the Where
of LINQ, or in the where T: DtoBase
, so I'll discuss both
Where in a generic method
public static void Merge<T>(ObservableCollection<T> target,
ObservableCollection<T> source)
where T : DtoBase
The <T>
after the identifier of the method Merget<T>
, tells us that this is a generic method
, which means that you can call it with all kinds of classes, as long as the class meets the where
condition:
where T : DtoBase
So you can call this method with any class T, as long as this class is (derived from) DtoBase
.
Examples:
class DtoBase {...};
class Derived : DtoBase {...};
ObservableCollection<DtoBase> myBases = ...
ObservableCollection<DtoBase> yourBases = ...
ObservableCollection<Derived> myDeriveds = ...
ObservableCollection<Derived> yourDeriveds = ...
Now you can call method Merge
, and replace every Occurence ot T
with anything that matches where T : DtoBase
. So the following are all valid:
Merge(myBases, yourBases);
Merge(myBases, myBases);
Merge(myDeriveds, yourDeriveds);
Merge(myBases, myDeriveds);
The following won't compile:
Merge(myBases, 4); // integer 4 is not a DtoBase
Merge("Hello", myBases); // string is not a DtoBase
Where in a LINQ statement
var targetHandles = target.Where(t => true);
The t
in this statement has nothing to do with the T in the generic method.
If you look at the definition of Enumerable.Where you'll see that it is a generic extension method.
public static IEnumerable<TSource> Where<TSource> (
this IEnumerable<TSource> source,
Func<TSource,bool> predicate);
Generic is discussed above: Where has a type TSource
, comparable with the T
we discussed earlier: wherever you see TSource
you should replace it with the same actual type.
We also see the keyword this
in front of the first parameter. This means that it is an extension method: you can put the first parameter in front of the method, like this:
IEnumerable<Product> products = ...
IEnumerable<Product> cheapProducts = products.Where(product => product.Price < 1);
This is the same as:
IEnumerable<Product> cheapProducts = Enumerable<Product>.Where(products,
product => product.Price < 1);
So extension method is just some fancy syntactic sugar. It makes it look as if it is a method of Product.
Now the fun part: the product
in product => product.Price < 1
. product is just an identifier with a well chosen name. I could also have written:
.Where(t => t.Price < 1);
the t => t.Price < 1
is called a lambda expression. It is some shorthand for a function definition: we create a method with input parameter t
, which returns t.price < 1
. The type of t
can be found in the generic:
public static IEnumerable<TSource> Where<TSource> (
this IEnumerable<TSource> source,
Func<TSource,bool> predicate);
In my example TSource
was a Product
, so the first parameter should be IEnumerable<Product>
. The return value is also IEnumerable<TSource>
.
IEnumerable<Product> products = ...
IEnumerable<Product> cheapProducts = products.Where(...);
predicate
is a Func<TSource, bool>
. This means: any function, with input TSource (which is in our case a Product), that returns a bool. So if you see somewhere:
Func<int, string, DateTime, Point>
Then it means: any method with three input parameters of type int, string, DateTime (in this order), which returns a Point:
Point MyFunct(int i, string txt, DateTime date) {...}
We've seen that the lambda expression t => t.Price < 1
is a method that takes a Product t
as input parameter and returns the value of t.Price < 1
, which obviously is a Boolean. Hence, this lambda expression matches Func<TSource,bool>.
IEnumerable<Product> cheapProducts = products.Where(product => product.Price < 1);
So what does this do? It takes every product
from the sequence of products
, and puts this in the function product => product.Price < 1
. In other words: from every product in the sequence of Products, it calculates the Boolean product.Price < 1
. It it is true, then we keep it, if not, we don't use it in the sequence returned by the Where
.
The effect is, that Where(product => product.Price < 1)
returns the sequence of all Products with a value for property Price that is less than 1.
Back to your question
Your Where
is a rather strange one.
IEnumerable<Target> targets = ...
targets.Where(t => true);
We know that targets
is a sequence of type Target. Hence, we know that the predicate is a method that takes as input one Target, and returns a bool:
bool Predicate(Target t) { return true}
Well, it does return a boolean. In fact, it always returns the same boolean, it doesn't even look at the input parameter t, it always returns true.
Where(t => true)
is similar to the following:
foreach(Target target in targets)
{
if (true) return target;
}
Well, this doesn't do a lot: it returns the complete input sequence:
IEnumerable<Target> targets = ...
var result = targets.Where(t => true);
We know that result
has the same elements, in the same order as targets
. The statement is not very useful.
Final remark
Whenever you have to define your own identifiers in LINQ statements, try to use plural nouns to identify sequences, use singular nouns to identify elements of the sequences. Try to avoid meaningless identifiers as t =>
.
typing less characters is not a good excuse for choosing bad identifiers!
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(),
})
This is a very big LINQ. You might not be familiar with GroupJoin
, but because I used plural and singular nouns, it is not very difficult to read:
In words: We have a sequence of Customers and a sequence of Orders. We GroupJoin the Customers and the Orders. This means, that from every Customer we take the Id, from every Order we take the CustomerId. We try to match them. From every customer with his zero or more Orders, we make one new object.
This new object contains the Id of the Customer, as well as his Name and his Address. From every Order in the sequence of Orders of this Customer, we make one new object. This new object contains the Date and the Total of the Order.
Because I used plural and singular nouns, this textual description corresponds very much with the LINQ. You don't have to wonder what t
and x
are. Customer
and OrdersOfThisCustomer
are much easier to understand what they stand for.
CodePudding user response:
The expression source.Where(x => true)
will return an IEnumerable
containing all the items in source
, no matter the value of each item. This is because true
is the entire predicate which determines which items should be filtered, and true
is always true
.
In most cases, such a predicate would be a function of the item (e.g. x > 3
), but it does not have to be.
Thereby, target.Where(t => true)
returns an IEnumerable<T>
containing all the items in target
. This way, a separate collection that can be worked with individually (without affecting target
) is created.
var targetHandles = target;
, on the other hand, would have created a reference to target
.