2

Say I have the following two functions:

void Foo(IEnumerable<string> bar)
{
  if(bar == null)
    return;
  foreach(var b in bar)
  {
    Console.Write(b);
  }
}    

and

void Foo(string bar)
{
   Foo(new string[] { bar });
}

Those two are ambiguous if I'm passing null as a parameter.

Is there a way to hint the compiler to disambiguate the overload? Any attribute or directive or something? In this case, I'd like the first function to be called. Something like:

[InCaseOfAmbiguityUseThis]
public void Foo(IEnumerable<string> bar)

So that if I Foo(null) the compiler will know where to look for and not complain.

I've been looking for a while and haven't found anything to the respect.

PS: I know I can use: Foo((IEnumerable<string>)null) but that's what I'm trying to avoid: the types in the real functions are quite long and use generic constraints (so I can't just inherit the type to make it shorter), so it dirties the code a lot.

I don't mind it being 'dirty' in the libraries (where I'm specifying those functions) but not on the actual business code (where I'm calling those functions).

Also, there might be a lot of these possibly ambiguous functions, so "a workaround to not make them ambiguous" is out of the question (that's what I'm using now, but I don't like having such boilerplate code)

Edit

I'm not looking for workarounds (i.e., making other functions with different or no parameters). I know all these ways, and as I specified, I'm already using that. Just wondering if it's actually possible to make the compiler disambiguate "automatically".

I'd prefer not to have a parameterless function (the real function is much more verbose passing a null parameter than passing no parameter at all):

Task<IEnumerable<TResult>> GetAsyncProjected<TResult>(
    IEnumerable<Expression<Func<T, bool>>> filters,
    Expression<Func<T, TResult>> projection,
    IEnumerable<string> eagerLoadRelationships,
    CancellationToken cancellationToken,
    ServiceRefreshMode refreshMode);

Task<IEnumerable<TResult>> GetAsyncProjected<TResult>(
    Expression<Func<T, bool>> filter,
    Expression<Func<T, TResult>> projection,
    IEnumerable<string> eagerLoadRelationships,
    CancellationToken cancellationToken,
    ServiceRefreshMode refreshMode);

// ... plenty of overloads for each possible parameter ...

I do have another overload for each of those overloads without the filter/filters parameter but I'd prefer not to so that the caller knows it's passing null for the filter explcitly.

If there is no way, then I'll look for other ways (calling it GetAsyncFilteredProjected() if it has a parameter or something)

Athari
  • 33,702
  • 16
  • 105
  • 146
Jcl
  • 27,696
  • 5
  • 61
  • 92
  • No, there isn't. It is only ambiguous if you are using a null literal when invoking the method. Consider why you want to pass a null literal so often. Perhaps you want a parameterless overload. – Mike Zboray Oct 22 '14 at 16:27
  • 1
    Another possible alternative is to declare the methods as `params string[]`, which allows the caller to supply any number of strings in-line, including none at all (with the drawback that you cannot pass an `IEnumerable` to it without first doing `.ToArray()`). – Jeroen Mostert Oct 22 '14 at 16:30
  • @JeroenMostert that would only be valid if the parameter is the last parameter of the function (in the "real" case, it's not) – Jcl Oct 22 '14 at 16:35
  • Are you sure you are getting ambiguous error? I think it can't find the correct overload because it doesn't exist. Your line of code on `Foo(string)` is this: `Foo(new int[] { bar });` You can't pass `int[]` into an `IEnumerable` – TyCobb Oct 22 '14 at 16:37
  • @Jcl: you will, in general, get higher quality answers by posting the "real" case as your question. I don't think this particular case is so complicated that it needed to be simplified. – Jeroen Mostert Oct 22 '14 at 16:38
  • @TyCobb that was obviously a mistake when making the example – Jcl Oct 22 '14 at 16:43
  • @JeroenMostert I made an edit with a sample – Jcl Oct 22 '14 at 16:43
  • 2
    Rather than looking for a non existing mechanism to disambiguate the ambiguous, take this as an opportunity to redesign the interface. What you've got seems very confusing and complex. Any time you expect a caller to pass null is a flag indicating a possible code smell. – Eric Lippert Oct 22 '14 at 16:51
  • @EricLippert it actually makes a lot of sense in usage. I could just have them pass empty or single-element `IEnumerables` (that should be the default). Having an overload for a single object is a commodity for those cases (many) where the `filter` would be unique... having users not pass anything (i.e., an overload without the parameter) makes it non-verbose to the library user and I'd prefer them passing null explcitly. – Jcl Oct 22 '14 at 17:00
  • @EricLippert that said, if *you* say there's no such mechanism, then I'll take your word for it :-) – Jcl Oct 22 '14 at 17:03
  • @Jcl In your case, I'd consider creating `GetAsyncProjectedOptions` class. Or relying on named parameters. Your code sounds like a mess. – Athari Oct 22 '14 at 17:03
  • 1
    Well, there are ways to hint to the compiler but I don't think you'll like any of them. For example, put the less desired ambiguous overloads in a less derived class. C# disambiguation logic always prefers more to less derived. – Eric Lippert Oct 22 '14 at 17:06
  • 1
    Or, if the methods are static and the callers are in the class, put the callers and the more desired overloads in a nested class. C# disambiguation logic prefers closer to farther nesting. – Eric Lippert Oct 22 '14 at 17:08
  • @Jcl Everyone should take Eric's word when it comes to .NET =P – TyCobb Oct 22 '14 at 17:08
  • @Athari that would not help if I have to make helper functions (or constructors) for generating the `GetAsyncProjectedOptions` class. I'd be in the same problem unless I derive many types from that class (which I could just be doing from the class that has that `GetAsyncProjected` function). – Jcl Oct 22 '14 at 17:09
  • @EricLippert they are not static. I guess I'll just remove the single-object call and for those cases have the user do `new[]{ filter }` (which would allow them to pass `null` if desired) – Jcl Oct 22 '14 at 17:11
  • 1
    If you want it to be explicit, consider providing a `public const Foo EmptyFoo = null` and then encourage the caller to use the descriptive constant. This both resolves the ambiguity and makes the calling code readable. – Eric Lippert Oct 22 '14 at 17:12
  • @EricLippert in my case (since I'm using interfaces) a const is not possible, but I created a readonly property which does just that (`public IEnumerable>> EmptyFilter { get { return (IEnumerable>>) null; } }`). Now I do like that! – Jcl Oct 22 '14 at 17:17
  • 3
    @Jcl: Excellent. Or, even better, `return Enumerable.Empty>>();`, and actually represent an empty collection as an empty collection, rather than the absence of a collection. – Eric Lippert Oct 22 '14 at 18:15

1 Answers1

3

There's no way to customize overloading in C# like this. There're strict rules of choosing a method overload.

However, if you want to provide a version of the method without the argument, you can just create a new overload instead of passing null:

void Foo()
{
    Foo((string)null);
}

It would be more readable, as a bonus.

Also, if you can't modify original classes and they don't provide overloads you need, you can use extension methods. This will keep business logic clean.

Athari
  • 33,702
  • 16
  • 105
  • 146
  • That's what I'm doing now, it's precisely what I want to avoid – Jcl Oct 22 '14 at 16:27
  • 1
    @Jcl It isn't clear from the question whether you create overloads or cast null to specific types when you call methods. The first way is *the* way of avoiding ambiguity in C#. – Athari Oct 22 '14 at 16:29
  • I'm currently already doing the parameterless overload. In this particular case, I'd prefer not to have a parameterless overload, but if it's not possible, then so be it. Anyway, the question was clear: is it possible to hint the compiler to disambiguate automatically? If it's not possible, then I know all the "workarounds". – Jcl Oct 22 '14 at 16:34
  • @Jcl See the first parapgraph then. The answer is no. Such an attribute would be close to impossible to implement. Consider having methods A, B, C; one call is ambigous between A and B (and you prefer A), another is ambigous between B and C (and you prefer B). How would you decorate the methods with your attribute? – Athari Oct 22 '14 at 16:38
  • I was afraid the answer was "no", and I had foreseen those possible ambiguities, but there's lots of not-so-known features in C# and a brighter mind than I could have thought of a solution (that's why I use stackoverflow ;-) ) – Jcl Oct 22 '14 at 16:45
  • Accepted as answer because the first paragraph is true (I was hoping it wasn't :-) ) – Jcl Oct 22 '14 at 17:25