I import data from a csv file and then store it in a list (List<Customer> customers
). The data is added to the list by creating and adding objects. The objects are created using constructors which take the contents of the file as parameters, everything is string. The list is of type Customer from what I understand. I want to make an if statement to check whether user input (string) exists in the list.
I tried this code:
ImportData("CustomerData.csv");
Console.WriteLine("Please provide customer name:");
string customer_Name = Console.ReadLine();
//if (customers.Contains(customer_Name))
bool b = customers.Contains(customer_Name);
//if (customers.Any(customers.Contains(customer_Name)))
if (b)
Nothing works, I get the error CS1503 Argument 1: cannot convert from 'string' to 'MyNameSpace.Customer'. I don't really understand what it means. I think maybe it is because of the list being of <Customer>
type? I also have a class called Customer. I tried to change the list type to <string>
, it didn't help.
CodePudding user response:
customers
is a List<T>
where T is Customer
, your error is telling you that it can't convert a string
to the class type Customer
, what I think you want is to use Linq
to look for any Customer
objects who's property of xxx (Customer Name) equals the user input, you could do this, where you'd replace somestringproperty
with your classes property name:
bool b = customers.Any(a => a.somestringproperty == customer_Name);
CodePudding user response:
List.Contains
requires you to supply a customer object. As you've likely NOT overridden Equals and GetHashcode to allow two different instances of customer to be considered equal, it's probably pointless to use, as it would need you to find an instance of a customer you wanted in the list, then ask the list if it Contains the instance you found (which you already know)
IEnumerable.Any
requires you to supply a method or small function (often called a lambda) that operates on a customer object and returns true or false. This is more useful to your situation
The short form looks like:
if (customers.Any(c => c.CustomerName == customer_Name))
The c => c.CustomerName == customer_Name
is a mini method (lambda):
c
the argument to the method; implicitly a Customer (hence why I named itc
) for reasons I'll go into in a moment=>
the separator between arguments and body; C# uses it to understand that youre making a lambdac.CustomerName == customer_Name
a statement that resolves to a boolean
In long form it might look like:
bool SomeMethod(Customer c){
return c.CustomerName == customer_Name;
}
We can shorten this long form to the short form because the compiler can figure out a lot of the stuff:
- It's a rule that Any needs something that returns a boolean, so we don't need the
bool
. - It's implied that the input argument
c
is aCustomer
because the List is full ofCustomer
s. - We can ditch the
{ return ... }
because C# has expression bodies; "arguments on the left, then a=>
, then a single statement that resolves to the return value type we first mentioned" means we're allowed to skip using curly brackets and thereturn
keyword - When writing a lamdba we don't need to give it a name, because we never refer to it by name anywhere; it is purely embodied by its definition and only used by the one thing we give it to, so it can remain nameless
So our long form gradually boils down:
//long form: a class level method
bool SomeMethod(Customer c){
return c.CustomerName == customer_Name;
}
//shorter: expression body; this is still valid C# to put in a class and you could write all your "one line return" methods like this
bool SomeMethod(Customer c) => c.CustomerName == customer_Name;
//shortest: a lambda, this has to be stored in a variable or used in a method call that accepts a lambda - you cannot write this in a class level statemetn because the compiler needs some surrounding context to figure bits of it out
c => c.CustomerName == customer_Name
If you take a look at the docs for Any
it tells you waht you have to supply:
Here's how to decipher this:
Any
is a method that can be called on an IEnumerable
. List is IEnumerable
, so Any
can be called on a List. A List is full of objects that have a particular type, TSource
. TSource
would be replaced by whatever is in the list, in this case a Customer
for you. The word this
means it's an extension method; a trick that means it looks like you can call it directly on a List of customers:
//you could do this; call Any and pass customers in
Enumerable.Any(customers, ...)
//but its more normal, readable etc to do this:
customers.Any(...)
The use of this
on the first argument allows you to write like the latter, and the compiler will understand it to be the former. It lets you "make it look like you added a method to the thing youre calling it on, even though you didn't"
The more interesting part is the Func<TSource,bool>
argument. This means "A function that takes a TSource
and returns a bool
". Remember, TSource
for your List<Customer>
means "a Customer".
The last thing mentioned is the return type, everything else is an argument to a method. If you saw Funct<string, int, TSource, DateTime>
it would mean "A function or method that has 3 input arguments; a string
, int
and TSource
(Customer) in taht order, and must return a DateTime". Any method that matches this signatore is allowed to be supplied.
Circling back to Funct<TSource, bool>
for Any
; you have to supply a method that takes a TSource
(Customer, in your case) and returns a bool
.
This matches that:
private string customer_Name = "john" //class level variable
//method that matches the rule "takes a Customer, returns a bool"
bool SomeMethod(Customer c){
return c.CustomerName == customer_Name;
}
//can be passed to list's Any
customers.Any(SomeMethod)
This shorter form also matches that:
customers.Any(c => c.CustomerName == customer_Name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
also a method that "takes a customer, returns a bool"
Any
will take the method you give it and start looping the list, calling the method and passing in the current customer it is looping. As soon as it gets a true it stops and returns true, because it has discovered that "Any customer matches ..." is true
I get the error CS1503 Argument 1: cannot convert from 'string' to 'MyNameSpace.Customer'. I don't really understand what it means
Hopefully now you can see, if you do:
customers.Any("John")
customers.Any
needs you to provide "a method that takes a Customer and returns a bool" a.k.a Func<Customer,bool>
- you cannot provide a string like "John" because a string is not a Func<Customer,bool>
..
..and think about it. If you did provide a string like "John" - how does C# know that's the name you want to look up, and not the address?
Providing a method as an argument rather than providing data as an argument is perhaps the biggest thing to get your head round here; why do Microsoft write code that accepts methods instead of data? Because simply put, they cannot possibly code for all the variations of how people will use List, so instead they just say "We'll loop the list, calling some bit of code we cannot imagine - the bit that you supply - and if your code returns true, we'll return true"
It's natural that Microsoft write C# but there are bits missing that only you can know - hence why "passing methods around like data" is vital