11

I have an extension method like

public static void RemoveDetail<TMaster, TChild>(this TMaster master, TChild child)
        where TMaster : class, IMaster<TChild>
        where TChild : class, IDetail<TMaster>;

And I have two classes

public class Principal : IMaster<Permission>
{
        public virtual IEnumerable<Permission> Permissions { get; }
}

and

public class Permission : IDetail<Principal>

I call RemoveDetail from an Action accepted by method public static void Foreach<T>(this IEnumerable<T> source, Action<T> action);:

aPrincipal.Permissions.Foreach(x => aPrincipal.RemoveDetail(x));

ReSharper suggest me to replace this call with method group like

aPrincipal.Permissions.Foreach(aPrincipal.RemoveDetail);

This worked fine in VS2013 and previous but it fails on compilation in VS2015 with

'Principal' does not contain a definition for 'RemoveDetail' and no extension method 'RemoveDetail' accepting a first argument of type 'Principal' could be found (are you missing a using directive or an assembly reference?)

Anyone has a suggestion? Do I have to update all of the usages and make ReSharper to avoid this replacement?

dvas_dubna
  • 129
  • 5

3 Answers3

7

I was able to reproduce your problem

public interface IDetail<T>
{

}

public interface IMaster<T>
{

}

public class MyClass
{
    public void method()
    {
        Principal aPrincipal = new Principal();
        aPrincipal.Permissions.Foreach(x => aPrincipal.RemoveDetail(x)); // No suggestion from resharper
    }
}

public class Permission : IDetail<Principal>
{

}

public class Principal : IMaster<Permission>
{
    public virtual IEnumerable<Permission> Permissions { get; }
}

public static class Class
{
    public static void RemoveDetail<TMaster, TChild>(this TMaster master, TChild child)
        where TMaster : class, IMaster<TChild>
        where TChild : class, IDetail<TMaster>
    {

    }

    public static void Foreach<T>(this IEnumerable<T> source, Action<T> action)
    {

    }
}

The resharper does not suggest anything. so probably you have to update your Resharper to newer version. probably was a bug which is fixed now.

If this is not like the way you do. then you have to put more information.

Also Compiler gives same error when i try to convert it to method group. So this question may remain: Why Compiler cant do this?

Edit:

To fix this problem you have to call the RemoveDetail method from inside Principal. so make your Principal class like this.

public class Principal : IMaster<Permission>
{
    public virtual IEnumerable<Permission> Permissions { get; }

    public void RemoveDetail(Permission p)
    {
        Class.RemoveDetail(this, p);
    }
}

I think there is kind of ambiguous inside compiler (Possibly bug) that cant recognize RemoveDetail method. how ever compiler tries to find it inside Principal. so you can fix it with creating RemoveDetail inside Principal and call a static RemoveDetail from there.

Edit 2:

The problem is generic type constraint.

where TMaster : class, IMaster<TChild>

This makes compiler to look at class which implements IMaster<TChild> and thats Principal. if you remove this where clause it will solve the problem. otherwise the compiler expects that it should be Principal.

If you use lambda you remove this ambiguity.

aPrincipal.RemoveDetail // compiler expects property/field/method in Principal
                        // but compiler forgets method group and static generic method!
x => aPrincipal.RemoveDetail(x) // this is no more like property or field
                                //but a method that is considered static generic method!

So thats a C#6 bug

M.kazem Akhgary
  • 18,645
  • 8
  • 57
  • 118
  • 1
    If I replace `x => aPrincipal.RemoveDetail(x)` in above code with just `aPrincipal.RemoveDetail` it compiles under VS2013, C# 5.0, targeting .NET 4.5.2. Can anyone confirm that this will not compile under the new C# compiler? – Jeppe Stig Nielsen Sep 02 '15 at 11:58
  • I tested in Visual studio 2015. C# 6.0, targeting .NET 4.6 and it does not work with `aPrincipal.RemoveDetail` @JeppeStigNielsen – M.kazem Akhgary Sep 02 '15 at 12:01
  • I confirm the first form does compile, but the second form does not compile in VS2015 on 4.5.2. BTW if I comment out `where TMaster : class, IMaster` from RemoveDetail method, it compiles fine. I guess they changed the way extensions are inferred for method group. – demoncodemonkey Sep 02 '15 at 12:01
  • 3
    There must be a bug in the new C# compiler! – Jeppe Stig Nielsen Sep 02 '15 at 12:03
  • In the ***C# Language Specification***, the section (section no. 6.6 in my version) _Method group conversions_ of the chapter _Conversions_ makes it very clear that an extension method can be found from a method group in this way. So this is required to work. – Jeppe Stig Nielsen Sep 02 '15 at 12:12
  • I contacted Eric Lippert and asked him to have a loo at this thread. – Olivier Jacot-Descombes Sep 02 '15 at 12:17
  • @OlivierJacot-Descombes Does Eric Lippert have anything to do with the C# compiler any more? – Rawling Sep 02 '15 at 12:22
  • 1
    Note that `public virtual IEnumerable Permissions { get; }` is an auto-property without a `set` accessor. This is allowed in C# 6.0 only. If you want to try the above example under C# 5.0 or earlier, try e.g. to say `{ get; set; }` instead, to make that part compile. – Jeppe Stig Nielsen Sep 02 '15 at 12:22
  • @JeppeStigNielsen i have found the problem. its bug in C#6. see the new edit – M.kazem Akhgary Sep 02 '15 at 12:48
  • @M.kazem Akhgary thank you for the answer! That is exact what I have. Sorry for an odd way of asking, just my first time. – dvas_dubna Sep 02 '15 at 13:25
  • @Jeppe Stig Nielsen this is not auto-property, I just extracted it from metadata. Actually the property has backing field – dvas_dubna Sep 02 '15 at 13:27
  • I do not have C# 6.0 here. Can you call the extension method in an ordinary way. I.e. can you say `aPrincipal.RemoveDetail(new Permission());` in the bottom of the body of `method` method above? – Jeppe Stig Nielsen Sep 02 '15 at 13:38
  • 1
    @JeppeStigNielsen yes you can but it's not a method group – dvas_dubna Sep 02 '15 at 13:51
  • @M.kazemAkhgary just updated to Resharper 9.2 and it's the same. Switched off **convert lambda expression to method group** in Code Inspection | Inspection Severity as there is no other way – dvas_dubna Sep 02 '15 at 14:38
  • i have resharper 9.1.1. i will upgrade it and let you know the behavior. probably resharper got the bug again! i did not changed the options. and resharper suggests convert if i remove the where clause. @dvas_dubna – M.kazem Akhgary Sep 02 '15 at 14:46
  • @dvas_dubna i dont know why but resharper still does not suggest anything for me.how ever i hope this bug is reported and they will fix it soon. – M.kazem Akhgary Sep 02 '15 at 15:17
  • 1
    I have entered the example in dotnetfiddle.net. C# 6.0 (roslyn 1.0.0-rc): https://dotnetfiddle.net/6VjBpk and C# 5.0: https://dotnetfiddle.net/245npq but both examples show the same behavior. – Olivier Jacot-Descombes Sep 07 '15 at 12:11
  • 1
    @OlivierJacot-Descombes It does work on `Mono v3.10.0` : http://www.tutorialspoint.com/compile_csharp_online.php .how ever this does not compile with only getter property. but both your tests are working with only getter property. probably its a bug inside update on both versions C#6 and C#5. – M.kazem Akhgary Sep 07 '15 at 17:50
1

I gave a feedback on this subject to Microsoft on September the 3rd 2015 here:
https://connect.microsoft.com/VisualStudio/feedback/details/1747835/visual-studio-2015-extension-method-call-as-method-group

Today I got this feedback from Neal [MSFT]:

This is fixed in VS2015 Update 1.
It is tracked here: https://github.com/dotnet/roslyn/issues/4970

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
-2

I don't see how it worked in VS2013. RemoveDetail is a method of TMethod not your Action MyClass.

It should be something like

z.ForEach(x => master.RemoveDetail(x);
Richard Schneider
  • 34,944
  • 9
  • 57
  • 73
  • 2
    Yes it does. The extension method essentially works for any `object` as there is no constraint to the generic type `TMaster`. The compiler succesfully resolves `TMaster` to `MyClass`. – InBetween Sep 02 '15 at 10:31
  • full signature is `public static void RemoveDetail(this TMaster master, TChild child) where TMaster : class, IMaster where TChild : class, IDetail;` **this** here is an instance of master class, so it owrks – dvas_dubna Sep 02 '15 at 10:37
  • @dvas_dubna Does `MyClass` implement `IMaster`? – InBetween Sep 02 '15 at 10:40
  • @dvas_dubna please post an complete example that *does* reproduce the problem. As others noted, the (incomplete) code you provided compiles just fine in 2015 once some typos are fixed – Panagiotis Kanavos Sep 02 '15 at 10:40
  • @InBetween: I am assuming that `MyClass` does **NOT** implement `IMaster` and stand by my answer. – Richard Schneider Sep 02 '15 at 10:43
  • @RichardSchneider The code compiles. If `TMaster` has no constraint then *any* object is a valid choice: `public static Foo(this T obj)` will succesfully resolve to any `T`. Can you find a type for `T` that will not compile succesfully? The signature originally posted by the OP is essentially the same.. – InBetween Sep 02 '15 at 10:47
  • @InBetween But OP says `TMaster` has constraint `class, IMaster`. – Richard Schneider Sep 02 '15 at 10:51
  • @RichardSchneider That information was initially brought up by the OP in comments to your answer so how did you manage to know about this before answering? We got ourselves a time travel riddle here. – InBetween Sep 02 '15 at 10:53
  • @InBetween It was just an assumption. I've seen this master/child pattern in may places. – Richard Schneider Sep 02 '15 at 11:16