簡體   English   中英

“顯式接口實現的約束......”

[英]“Constraints for explicit interface implementation…”

我無法弄清楚為什么以下不會工作,任何想法? 公共接口IFieldSimpleItem {}

public interface IFieldNormalItem : IFieldSimpleItem
{ }

public class Person
{
    public virtual T Create<T>()
        where T : IFieldSimpleItem
    {
        return default(T);
    }
}

public class Bose : Person
{
    public override T Create<T>()
        where T : IFieldNormalItem //This is where the error is
    {
        return default(T);
    } 
}

我這樣做的原因是,如果開發人員繼承自Bose,Bose依賴於創建至少為IFieldNormalItem的實例。 以下僅依賴於IFieldSimpleItem,但上述內容應強制它至少為IFieldNormalItem。

public class Person
{
    public virtual IFieldSimpleItem Create() 
    {
        return null;
    }
}

public class Bose : Person
{
    public override IFieldSimpleItem Create()  
    {
        return null;
    } 
}

干杯安東尼

我很確定你使用編譯器和泛型來節省一些運行時檢查是不走運的。 您不能覆蓋尚不存在的內容,並且您不能對相同的方法使用不同的返回類型。

我不能說我完全理解你的動機,但它有技術價值。

我的第一次嘗試是使用具有非虛擬公共接口的基類,然后使用另一個受保護的虛擬方法CheckCreatedType ,它允許鏈中的任何東西在調用基類Create之前檢查類型。

public class A
{
    public IFieldSimpleItem Create()
    {
        IFieldSimpleItem created = InternalCreate();
        CheckCreatedType(created);
        return created;
    }

    protected virtual IFieldSimpleItem InternalCreate()
    {
        return new SimpleImpl();
    }
    protected virtual void CheckCreatedType(IFieldSimpleItem item)
    { 
        // base class doesn't care. compiler guarantees IFieldSimpleItem
    }
}
public class B : A
{
    protected override IFieldSimpleItem InternalCreate()
    {
        // does not call base class.
        return new NormalImpl();
    }
    protected override void CheckCreatedType(IFieldSimpleItem item)
    {
        base.CheckCreatedType(item);
        if (!(item is IFieldNormalItem))
            throw new Exception("I need a normal item.");

    }
}

以下內容在運行時檢查基類。 無法解決的問題是你仍然需要依賴被調用的基類方法。 行為不端的子類可以通過不調用base.CheckCreatedType(item)來中斷所有檢查。

替代方法是硬編碼基類內所有子類的所有檢查(壞),或以其他方式外部化檢查。

嘗試2 :(子)類注冊他們需要的檢查。

public class A
{
    public IFieldSimpleItem Create()
    {
        IFieldSimpleItem created = InternalCreate();
        CheckCreatedType(created);
        return created;
    }

    protected virtual IFieldSimpleItem InternalCreate()
    {
        return new SimpleImpl();
    }

    private void CheckCreatedType(IFieldSimpleItem item)
    {
        Type inspect = this.GetType();
        bool keepgoing = true;
        while (keepgoing)
        {
            string name = inspect.FullName;
            if (CheckDelegateMethods.ContainsKey(name))
            {
                var checkDelegate = CheckDelegateMethods[name];
                if (!checkDelegate(item))
                    throw new Exception("failed check");
            }
            if (inspect == typeof(A))
            {
                keepgoing = false;
            }
            else
            {
                inspect = inspect.BaseType;
            }
        }
    }

    private static Dictionary<string,Func<IFieldSimpleItem,bool>> CheckDelegateMethods = new Dictionary<string,Func<IFieldSimpleItem,bool>>();
    protected static void RegisterCheckOnType(string name, Func<IFieldSimpleItem,bool> checkMethod )
    {
        CheckDelegateMethods.Add(name, checkMethod);
    }
}
public class B : A
{
    static B()
    {
        RegisterCheckOnType(typeof(B).FullName, o => o is IFieldNormalItem);
    }

    protected override IFieldSimpleItem InternalCreate()
    {
        // does not call base class.
        return new NormalImpl();
    }
}

檢查由子類注冊一個委托在基類中調用,但沒有基類知道所有規則。 另請注意,它仍然是非虛擬公共接口,它允許基類在返回結果之前檢查結果。

我假設你正試圖抓住這是一個開發人員錯誤。 如果它適用,您可以使用System.Diagnostics.Conditional("DEBUG")]裝飾運行時檢查方法,允許Release版本跳過檢查。

我對泛型的了解並不完美,所以也許這是不必要的。 但是,這里的檢查不一定是單獨的類型:這可以適用於其他用途。 例如,在Register..中傳遞的委托不必僅僅檢查引用是否是特定類型'

*請注意,如上所述,在類型名稱上創建字典可能不太好; 這個工作有點簡單,以說明使用的機制。

這是不允許的,因為它違反了Liskov Substitution Principle。

假設你有另一個界面:

public interface IFieldSuperItem : IFieldSimpleItem

然后你可能會這樣做

Person p = new Boss();
p.Create<IFieldSuperItem>();

第二行中的調用,雖然與Create in Person的定義兼容,但顯然與Boss中定義的不兼容(僅適用於IFieldNormalItem及其子類)。

我認為問題是你覆蓋以前定義的方法。 因此,您有效地嘗試更改方法的定義,這是不允許的。 您唯一的選擇是創建一個新方法,例如

public class Bose : Person
{
    public virtual T CreateNormal<T>()
        where T : IFieldNormalItem //This is where the error is
    {
        return default(T);
    } 
}

或要求Person類上的普通字段,或動態進行驗證。

您似乎無法更改方法的定義,但是您可以使您的類通用而不是創建方法嗎?

public class Person<T> where T : IFieldSimpleItem
{
    public virtual T Create()
    {
        return default(T);
    }
}

public class Bose<T> : Person<T> where T : IFieldNormalItem
{
    public override T Create()
    {
        return default(T);
    } 
}

更改通用約束會更改方法簽名,如果您覆蓋虛擬,則不允許這樣做。

我想你可能需要將Create方法拆分成一個單獨的類:

public interface IFieldSimpleItem { }

public interface IFieldNormalItem : IFieldSimpleItem{ }

public interface IFieldCreator<TField, TPerson> where TField : IFieldSimpleItem where TPerson : Person
{
    TField Create(TPerson person);
}

public class Person
{
}

public class Bose : Person
{
}

public class PersonFieldCreator : IFieldCreator<IFieldSimpleItem, Person> 
{
    public IFieldSimpleItem Create(Person person) { return null; }
}

public class BoseFieldCreator : IFieldCreator<IFieldNormalItem, Bose>
{
    public IFieldNormalItem Create(Bose person) { return null; }
}

那這個呢:

public interface IFieldNormalItem : IFieldSimpleItem
{ }

public class Person<T> where T : IFieldSimpleItem
{
    public virtual T Create()
    {
        return default(T);
    }
}

現在你可以擁有Person<IFieldSimpleItem> (對應於Person )或Person<IFieldNormalItem> (對應於Bose )。

下面的代碼足以覆蓋。 類型T已經表示需要由基類Person中的IFieldSimpleItem實現。

public class Bose : Person
{
    public override T Create<T>()
        // where T : IFieldNormalItem // You don't need this line.
    {
        return default(T);
    } 
}

編輯:我完全錯了這個問題,所以上面的代碼不會解決這個問題。 你唯一要做的就是; 不要通過“覆蓋”但“虛擬”覆蓋Create方法。

public class Bose : Person
{
    public virtual T Create<T>()
        where T : IFieldNormalItem
    {
        return default(T);
    } 
}

最簡單的例子是這打破了多態性。 如果你有一個Person的集合,其中一個或多個項目屬於Bose類型,一旦它擊中Bose就會崩潰。

Person[] people;
[...initialize this somewhere...]

foreach(Person p in people)
  p.Create<IFieldSimpleItem>();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM