繁体   English   中英

C#没有可用的扩展方法来覆盖特定类型

[英]C# No accessible extension method for override with specific type

如果已经问过了,请原谅我。 我搜索了它,但找不到任何东西。

似乎编译器对此代码感到困惑

public abstract class C1
{
    public int c1Prop;
}

public class C2 : C1
{
    public int c2Prop;
}

public abstract class P1
{
    public abstract void Run<T>(T c) where T : C1;
}

public class P2 : P1
{
    public override void Run<C2>(C2 c) 
    {
        c.c1Prop = 1; //Is recognized
        c.c2Prop = 2; //Is NOT recognized and is an error
    }
}

我不明白为什么这在功能级别上不起作用。 由于C2扩展了C1,因此它不会违反where检查,但是在重写方法中仍无法识别C2类型。

在某些背景下,我在Unity ScriptableObject中使用了类似的模式,该模式不适用于通用类,因此无法将通用类型上移到类级别。 虽然将它移到那里似乎可以解决问题。

我想出的另一种可能的解决方法是一起摆脱通用方法,而支持转换。 但是,这似乎不像泛型那样具有表现力。 而且在整个Run方法中的多个地方强制转换c会很烦人。

当您说void Run<C2>(C2 c)您说的是C2泛型类型 ,而不是具体类型C2 为了更清楚一点,将C2更改为T

public override void Run<T>(T c)
{
    c.c1Prop = 1; //Is recognized
    c.c2Prop = 2; //Is NOT recognized and is an error
}

您可以访问c1Prop的原因是类型约束where T : C1在层次结构中较早。

一种解决方法是使P1本身具有通用性:

public abstract class P1<T> where T : C1
{
    public abstract void Run(T c);
}

这使得P2看起来像这样:

public class P2 : P1<C2>
{
    public override void Run(C2 c)
    {
        c.c1Prop = 1;
        c.c2Prop = 2;
    }
}

造成混乱的最初原因是在Run覆盖范围内, C2类型参数 - 不是称为C2的类。 通过在覆盖方法的声明中将其保留为T可以使这一点很清楚:

public class P2 : P1
{
    // Changed type parameter name from C2 to T for clarity
    public override void Run<T>(T c) 
    {
        c.c1Prop = 1;
        c.c2Prop = 2;
    }
}

那是绝对等效的代码,但是更清楚了发生了什么。

现在T受到where T : C1约束,这就是c.c1Prop工作方式-但c不会是C2完全可行的 例如,我可以写:

class OtherC1 : C1 {}

P2 p2 = new P2();
p2.Run(new OtherC1());

显然,这不能与您当前的代码一起使用c2Prop中没有OtherC1

听起来您可能希望P1是通用的,而不是Run方法。 你可以有:

public abstract class P1<T> where T : C1
{
    public abstract void Run(T c);
}

public class P2 : P1<C2>
{
    public override void Run(C2 c) 
    {
        c.c1Prop = 1; //Is recognized
        c.c2Prop = 2; //Is NOT recognized and is an error
    }
}

然后将进行编译,并且所有代码都将知道您只能P2.Run提供一个C2 (或更多派生类)。 因此,我们先前使用OtherC1示例将不再编译(这是我们想要的)。

您的方法签名指出该方法应该是通用的,并且不能仅对某些具体类型覆盖它,您可以执行以下操作:

    public class P2 : P1
    {
        public override void Run<T>(T c)
        {
            c.c1Prop = 1; //Is recognized since you have where T : C1 clause
            var c2 = c as C2;
            if (c2 != null)
            {
                c2.c2Prop = 2;
            }
        }
    }

虽然我支持@DavidG的方法,但是如果您不想在类上使用泛型声明,则可以使用此方法

public interface IC1
{
    int prop1 { get; set; }
}

public interface IC2
{
    int prop2 { get; set; }
}

public abstract class C1 : IC1
{
    #region Implementation of IC1

    public int prop1 { get; set; }

    #endregion
}

public class C2 : C1, IC2
{
    #region Implementation of IC2

    public int prop2 { get; set; }

    #endregion
}

public abstract class P1
{
    public abstract void Run<T>(T c) where T : IC1, IC2;
}

public class P2 : P1
{
    public override void Run<T>(T c)
    {
        c.prop1 = 1; 
        c.prop2 = 2; 
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM