簡體   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