20

Does not work:

Func<string, byte[]> getFileContents = (Mode != null && Mode.ToUpper() == "TEXT")
            ? TextFileContents
            : BinaryFileContents;

private static byte[] BinaryFileContents(string file)
{
    return System.IO.File.ReadAllBytes(file);
}

private static byte[] TextFileContents(string file)
{
    using (var sourceStream = new StreamReader(file))
    {
        return Encoding.UTF8.GetBytes(sourceStream.ReadToEnd());
    }
}

Error is

no implicit conversion between method group and method group

Works:

Func<string, byte[]> getFileContents2;
if (Mode != null && Mode.ToUpper() == "TEXT")
{
   getFileContents2 = TextFileContents;
}
else
{
   getFileContents2 = BinaryFileContents;
}

I'm just curious why? Am I missing something?

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
Aaron Anodide
  • 16,906
  • 15
  • 62
  • 121

1 Answers1

29

Anonymous functions and method groups don't have types in themselves - they are merely convertible to delegate types (and expression tree types for some lambda expressions).

For the conditional operator to determine the overall type of the expression, at least one of the second or third operands has to have a type. You could cast either of them to Func<string, byte[]> and the compiler would find that it could convert the other one to the same type, and be happy.

For example:

Func<string, byte[]> getFileContents = DateTime.Now.Hour > 10
    ? (Func<string, byte[]>) TextFileContents
    : BinaryFileContents;

From section 7.14 of the C# 5 spec:

The second and third operands, x and y, of the ?: operator control the type of the conditional expression.

  • If x has type X and y has type Y then [...]
  • If only one of x and y has a type [...]
  • Otherwise, no expression type can be determined, and a compile-time error occurs.
Community
  • 1
  • 1
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • nice, and with your solution one can even switch to var – Aaron Anodide Jun 26 '15 at 19:47
  • 1
    I did not truly understand this nuance, "... don't have types in themselves - they are merely convertible *to* delegate types...", until now. Worth way more than +1. – William Jun 26 '15 at 19:50
  • 2
    @billisphere: Glad it helped :) It took me a *long* time to get that, too. (Same goes for the `null` literal, btw. *Most* expressions do have types.) – Jon Skeet Jun 26 '15 at 19:50
  • a _long_ time? It took you 4 minutes to answer his question :) Nice catch by the way – Maxime Tremblay-Savard Jun 26 '15 at 19:58
  • 1
    @MaximeTremblay-Savard: I mean in general, since starting to learn C#. I knew it before I started answering this particular question :) – Jon Skeet Jun 26 '15 at 19:59
  • 2
    @MaximeTremblay-Savard Jon answers this question at least monthly... You should be find exact duplicates by Jon or Eric Lippert relatively easy - like http://stackoverflow.com/questions/263151/lambdas-and-the-conditional-operator-weird-issue – Alexei Levenkov Jun 26 '15 at 20:06
  • I'm curious if the fact that anonymous functions and method groups do not have types is in the class of "we could flip that bit and it would work" or if there's something that would be complicated to a point that it would be not worth it to flip that bit... – Aaron Anodide Jun 29 '15 at 05:24
  • @AaronAnodide: Until you use an anonymous function in a context, you really can't tell what the type would be. For example, consider `x => x.Length > 5`. Is that a `Predicate`? Is it a `Func`? Is it an expression tree? It's just an expression which is convertible to any of those. – Jon Skeet Jun 29 '15 at 05:46
  • @JonSkeet, I guess what I'm getting at is, what if the spec read, "x has or is convertible to"? In my example, if the l-value were 'var' then the fact that `x => x.Length > 5` is convertible to 3 different things would cause ambiguity, however, with the type specified, it seems to me there's this is more of a possible future feature of the language than a fundamental impracticality. Then again, C# won't infer the F from the l-value, so maybe the same reason prevents my reasoning... – Aaron Anodide Jun 29 '15 at 07:45
  • @AaronAnodide: But the problem is that it's one step removed in this case. For a simple assignment it wouldn't be too bad, but for the conditional operator itself to sometimes not have a type would make the typelessness spread - imagine if you used it as an argument for a method call, so it would then need to be in overloading etc. Basically I suspect the language would end up being much more complicated for very little benefit. – Jon Skeet Jun 29 '15 at 07:47
  • funny... i'm up too late - you're probably right on time over there in England:) – Aaron Anodide Jun 29 '15 at 07:48
  • @JonSkeet: Using `x => x.Length > 5` as `Func` gives a "cannot implicitly convert type 'bool' to 'int'" error. `Func` is fine and still works as a valid secondary example. – Brian Jul 01 '15 at 04:57
  • @Brian: Whoops, sorry - was probably thinking of `x => x.Length`, but then of course that couldn't be a `Predicate`. Shame I can't edit comments that old :( – Jon Skeet Jul 01 '15 at 05:57