Visual Studio 2015扩展方法调用作为方法组

11

我有一个类似下面代码的扩展方法:

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

我有两个类

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

以及

public class Permission : IDetail<Principal>

我从一个由方法 public static void Foreach<T>(this IEnumerable<T> source, Action<T> action) 接受的操作中调用了 RemoveDetail:

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

ReSharper建议我将此调用替换为方法组,例如:

aPrincipal.Permissions.Foreach(aPrincipal.RemoveDetail);

这在 VS2013 及之前的版本中运行良好,但在 VS2015 中编译失败,显示:

'Principal' 不包含名为 'RemoveDetail' 的定义,并且找不到接受类型为 'Principal' 的第一个参数的扩展方法 'RemoveDetail'(您是否缺少 using 指令或者程序集引用?)

有人有建议吗? 我是否需要更新所有用法并让 ReSharper 避免这种替换?


1
它可以在我的VS 2015中编译。 - Kamil Budziewski
这可能是个愚蠢的问题,但你的项目是否针对正确的框架版本? - 3dd
请发布一个完整的示例。我也无法使用List<T>.ForEach复现这个问题。z的类型是什么?您确定没有漏掉引用或静态关键字吗? - Panagiotis Kanavos
更新为更具体的用法 - dvas_dubna
2
因为这个问题可能发现了VS2015编译器中的一个错误,所以我点了赞,只是希望它能以更好的方式提出... - demoncodemonkey
显示剩余2条评论
3个回答

7
我能够重现你的问题。
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)
    {

    }
}

ReSharper没有提供任何建议。因此,你可能需要将Resharper更新到较新的版本。这可能是一个已修复的错误。

如果这不是你的解决方案,请提供更多信息。

当我尝试将其转换为方法组时,编译器也会给出相同的错误。因此,这个问题可能仍然存在:为什么编译器不能做到这一点?

编辑:

要解决这个问题,你需要从Principal类内部调用RemoveDetail方法。因此,将你的Principal类更改为以下内容。

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

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

我认为编译器内部存在一些模糊(可能是错误),无法识别RemoveDetail方法。但编译器会尝试在Principal中找到它。因此,您可以通过在Principal中创建RemoveDetail并从那里调用静态的RemoveDetail来解决此问题。 编辑2: 问题是通用类型约束。
where TMaster : class, IMaster<TChild>

这使得编译器查看实现了 IMaster<TChild> 接口的类,也就是 Principal。如果您删除此 where 子句,则会解决问题。否则编译器会期望它应该是 Principal

如果使用 lambda 表达式,您可以消除这种不确定性。

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!

所以这是C#6的一个bug。

1
如果我在上面的代码中用aPrincipal.RemoveDetail替换x => aPrincipal.RemoveDetail(x),则它可以在VS2013、C# 5.0、目标.NET 4.5.2下编译。有人能确认这在新的C#编译器下不会编译吗? - Jeppe Stig Nielsen
3
新的C#编译器一定有bug! - Jeppe Stig Nielsen
1
请注意,public virtual IEnumerable<Permission> Permissions { get; }是一个没有set访问器的自动属性。这仅适用于C# 6.0。如果您想在C# 5.0或更早的版本中尝试上述示例,请尝试使用{ get; set; }来使该部分编译通过。 - Jeppe Stig Nielsen
1
@JeppeStigNielsen 是的,你可以这样做,但它不是一个方法组。 - dvas_dubna
1
我已经在dotnetfiddle.net上输入了示例。C# 6.0 (roslyn 1.0.0-rc):https://dotnetfiddle.net/6VjBpk 和 C# 5.0:https://dotnetfiddle.net/245npq,但两个示例都显示相同的行为。 - Olivier Jacot-Descombes
显示剩余13条评论

1

-2

我不明白它在VS2013中是如何工作的。RemoveDetailTMethod的一个方法,而不是您的操作MyClass

应该像这样:

z.ForEach(x => master.RemoveDetail(x);

2
是的,它可以。扩展方法基本上适用于任何“对象”,因为没有对泛型类型“TMaster”的约束。编译器成功地将“TMaster”解析为“MyClass”。 - InBetween
完整的签名是: public static void RemoveDetail<TMaster, TChild>(this TMaster master, TChild child) where TMaster : class, IMaster<TChild> where TChild : class, IDetail<TMaster>; this 在这里是主类的一个实例,所以它有效。 - dvas_dubna
@RichardSchneider 代码编译通过。如果“TMaster”没有任何约束,则任何对象都是有效选择:“public static Foo <T>(this T obj)”将成功解析为任何“T”。您能找到一个类型的“T”不会成功编译吗?OP最初发布的签名基本上是相同的。 - InBetween
@InBetween 但是OP说TMaster有约束class,IMaster<TChild> - Richard Schneider
@RichardSchneider,那些信息最初是由OP在您的答案评论中提出的,那么您是如何在回答之前就知道这些信息的呢?我们在这里遇到了一个时间旅行谜题。 - InBetween
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接