[英]“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.