16

I found this sample of code on SO (can't remember from where :/) that allowed me to check for line code arguments when launching my application :

if (e.Args.Length == 0 || e.Args.Any("-show".Contains))
{
  //show interface...
}

I just can't seem to understand how the "-show".Contains works. And if there's any difference with a (classic) x => x.Contains('"-show") (except for the evident typing gain).

Works like a charm but I'd like to understand why, I feel like something big is hapening.

Mong Zhu
  • 23,309
  • 10
  • 44
  • 76
Phoque
  • 217
  • 3
  • 14
  • 2
    Link expression Any(...) takes the argument `Func predicate` and Method `Contains(...)` satisfies exact that function signature. Taking a string as parameter and returning a bool. So no need to wrap the Contains in a anonymous function. – Thomas K May 03 '18 at 09:18
  • 3
    Side note: this is a *terrible* way of checking argument value. – Thomas Ayoub May 03 '18 at 13:24
  • Yes, as i read the answers I realized that it's not a good solution (and changed it back to a lambda expression(x=>x=="-show"). – Phoque May 03 '18 at 13:29

4 Answers4

20

This:

.Any("-show".Contains)

is basically shorthand for this:

.Any(s => "-show".Contains(s))

The Any method takes a delegate as a parameter and you can create that delegate in a number of ways. The first snippet uses a method group while the second uses a Lambda.

It's not really accurate to say that the first is shorthand for the second because method groups predate Lambdas, but if you think in terms of Lambdas when calling LINQ methods like Any then it is effectively the case.

jmcilhinney
  • 50,448
  • 5
  • 26
  • 46
  • 1
    Note that method group only work when the delegate receives maximum one parameter. When there is more parameters the method group can't be used. – CodeNotFound May 03 '18 at 09:15
  • 2
    It's not a shorthand, it's just exactly what the delegate accepts – adjan May 03 '18 at 09:15
  • So basically, that's a delegate construction from a method group (hence no parentheses) here? I'm not really aware with delegates or method groups. – Phoque May 03 '18 at 10:00
  • Yes, that's it. It's basically the same as using `AddressOf` in VB. – jmcilhinney May 03 '18 at 10:02
  • @Phoque Conceptually, it's just using the bound method (A bound method is just an instance method attached to a particular instance, so a definition for `this` inside it.) as an object and passing it as an argument. Lambdas are just inline definitions of methods. Though I won't vouch for that reflecting the underlying implementation. – jpmc26 May 03 '18 at 10:45
  • Personally, I would use the full lambda syntax. I find the method group format to be clever, but more difficult to read. This is especially true if any newer developers will have to read the code. – Bradley Uffner May 03 '18 at 17:36
  • @BradleyUffner I'd disagree: the lambda vairable simply adds noise to a simple check, and anyone who is confused by this can simply hover their cursor over the `Contains` and IntelliSense will tell you exactly what is being called. As for "this may confuse newer developers" - I chucked that advice out the window years ago. If it confuses them, and they're any good, they'll either figure it out themselves, research it themselves, or ask. If they don't do any of those things, you probably don't want them touching your code. – Ian Kemp May 03 '18 at 17:46
  • @IanKemp The point is code isn't *meant* to take long to figure out. That's time better put into the rest of development. They *ought* to be able to, if they're worth their salt, but they shouldn't spend longer than strictly necessary. Hard-to-understand code is missing the point of having a human-readable language to begin with. – Phi May 06 '18 at 16:23
4

As @jmcilhiney already said, it shorthand for:

.Any(s => "-show".Contains(s))

Contains is a function accepting 1 parameter which is of type string (and returns a boolean). Any() in this case wants a function that needs 1 param which is a string and that returns a boolean. So rather than adding an extra lambda warapper s=>, you can directly return .Contains

In technical terms this is a:

Func<string, boolean> //1 param string, output: boolean

Note that this code matches any argument that is a part of -show

thus either of the following arguments do match!

-show
-s
sh
ow
h
w
// etc..
Joel Harkes
  • 10,975
  • 3
  • 46
  • 65
  • 1
    *any argument that **is** a part of `-show`* - `foos` contains both `o` and `s` but it will not match – Rafalon May 03 '18 at 09:16
4

Any() expects a Func<TSource, bool> delegate. So any function that returns a bool and takes an argument that is of the same type as the elements of the collection (string in your case) can be applied. This can be an existing function, or a lambda expression.

The signature of String.Contains is

bool Contains(string s)

That's why you can pass it to Any()

adjan
  • 13,371
  • 2
  • 31
  • 48
1

The equivalent would be x => "-show".Contains(x) rather than what you've shown.

After that you'll realise that all you're doing by introducing the x based lambda is to create a function accepting a single string argument and returning a bool and wrapping a function that accepts a single string and returns a bool. There's no need for the wrapper.

Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448