1

I have code looking like this:

string target = ListOfTabs.FirstOrDefault(e.Target.Location.OriginalString.Contains);

I didn't write the code and I am trying to understand what it does. I saw there is a method called Contains() but cannot understand here why there are no () after the Contains. Is this a different thing? Can someone explain what this code is doing?

enter image description here

Sathish Guru V
  • 1,417
  • 2
  • 15
  • 39
Alan2
  • 23,493
  • 79
  • 256
  • 450

3 Answers3

5

FirstOrDefault basically takes a function as argument. Writing

ListOfTabs.FirstOrDefault(e.Target.Location.OriginalString.Contains);

is practically the same as writing

ListOfTabs.FirstOrDefault(x => e.Target.Location.OriginalString.Contains(x));

Both Contains and x => Contains(x) are functions, so it works!

Corentin Pane
  • 4,794
  • 1
  • 12
  • 29
  • 1
    Also, even if the second line of code is more readable and easy to understand it have a cost as it create new objects. – Orace Feb 06 '20 at 09:03
  • 3
    @Orace [Not true](https://sharplab.io/#v2:EYLgHgbALAPgAgBgARwIwDoAyBLAdgRwG4BYAKDgGYUAmJAYSQG8ylWkAHAJ2wDcBDAC4BTFKmQB7bgHM8fADYAVPpylCBJUmyQs2lUcgCyACjQIA2gF0kYAJRMdW1nADs19ADFsnAM4CA8pwAIkIAZnwArnICRmBIALwAfEiS2DK48koqauh04rgCfHjeMTY2GloAvg7amrpUpkgAciZiltZ2zLWOKK5gHl6+AcFhkdEpaRnKqgI5eQVFZdVVpBVAA=) – canton7 Feb 06 '20 at 09:16
  • @canton7 The link you provided shows the compiler for lambda-version unnecessarily defines a new `[CompilerGenerated]` method `b__1_0(String)` - while this *won't* result in any GC heap allocations (and the JIT will probably inline the function) it's disappointing that the C# compiler doesn't automatically inline trivial lambdas. – Dai Feb 06 '20 at 09:23
  • @Orace That question compares creating a delegate vs not creating one, and the delegate is capturing. Neither of the delegates in the question above are capturing – canton7 Feb 06 '20 at 09:23
  • @Dai Yeah I found that odd -- I already [asked about that on gitter](https://gitter.im/dotnet/csharplang?at=5e3bdab1f301780b836c21b5). I'm presuming it would be a pointless optimization. It's also unusual that the compiler isn't caching the generated delegate: normally it does – canton7 Feb 06 '20 at 09:24
  • @canton7 in some case there is a context capture : https://sharplab.io/#v2:EYLgHgbALAPgAgBgARwIwDoAyBLAdgRwG4BYAKDgGYUAmJAYSQG8ylWXWUq1kBZACm5IA9gCdsAczwBDADYAVKSPEBTAC4AaFKgQBtALpIwASibsOHOAHZD6AGLYRAZ1UB5EQBFlAMykBXGap8YEgAvAB8wmKSuLIKSmrodEK4qlJ4jkFGRiSk5gC+ZkiFlFrIAHIC2pES0vKKKhql+oYmzLnmFtZgdg7Obp4+/oGiNTF18aqJyanp2YUFpHlAA=] – Orace Feb 06 '20 at 09:24
  • @Orace Correct, but not in this answer, and certainly not in "the second line of code", as you claimed – canton7 Feb 06 '20 at 09:25
  • @canton7 We don't know where `e` is declared, is it in the local context or is it a field of current object ? By case convention it's probably a parameter of the current method and belong to the local context and need to be captured. – Orace Feb 06 '20 at 09:27
  • @Orace That's fair. Your original comment could do with some clarification, then – canton7 Feb 06 '20 at 09:28
1

You dont need it because FirstOrDefault take a Func in parameter. Since Contains match the type asked, you can pass Contains like you would pass any argument to any other function.

FirstOrDefault's signature is:

public static TSource FirstOrDefault<TSource> (this System.Collections.Generic.IEnumerable<TSource> source, Func<TSource,bool> predicate);

You are used to write:

var target = ListOfTabs.FirstOrDefault(x => e.Target.Location.OriginalString.Contains(x));

Which is the same as

Func<string, bool> predicate = x => e.Target.Location.OriginalString.Contains(x);
var target = ListOfTabs.FirstOrDefault(predicate);

Which is the same as

Func<string, bool> predicate = e.Target.Location.OriginalString.Contains;
var target = ListOfTabs.FirstOrDefault(predicate);

At that point, you can pass Contains directly:

var target = ListOfTabs.FirstOrDefault(e.Target.Location.OriginalString.Contains);
aloisdg
  • 22,270
  • 6
  • 85
  • 105
0

FirstOrDefault accepts a Func<TSource, bool> as it's argument.

Normally this would be a lambda expression, but as string.Contains can be cast to a Func<string, bool>, it can be passed to the method directly (assuming ListOfTabs is IEnumerable<string>).

In the implementation of FirstOrDefault, the () syntax will be used to actually execute string.Contains.

Johnathan Barclay
  • 18,599
  • 1
  • 22
  • 35