2008-11-12 64 views
5

我想不通爲什麼以下不會工作,任何想法? 公共接口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); 
    } 
} 

我爲什麼這樣做的原因是因爲,如果一個開發商從繼承百色,百色依賴於實例被創建至少是IFieldNormalItem的事實。而下面只依賴於它是IFieldSimpleItem但上面的應該強制其至少IFieldNormalItem。

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

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

乾杯 安東尼

回答

2

我敢肯定你的運氣了儘可能使用編譯器和泛型爲您節省一些運行時檢查。你不能重寫那些不存在的東西,並且你不能有相同方法的不同返回類型。

我不能說我完全理解你的動機,但它具有技術優勢。

我的第一次嘗試是使用具有非虛擬公共接口的基類,然後使用另一個受保護的虛擬方法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")]修飾運行時檢查方法,允許發佈版本跳過檢查。

我對泛型的瞭解並不完美,所以也許這是不必要的。然而,這裏的支票不一定是單獨的類型:這可以適用於其他用途。例如在Register..通過的代表不必檢查參考是一個特定的類型'

*請注意,它可能不是很好,如上面寫的類型名稱創建字典;爲了說明使用的機制,這個工作有點簡單。

1

我認爲問題是,你重寫先前定義的方法。所以有效地嘗試改變方法的定義,這是不允許的。您唯一的選擇是創建一個新的方法,例如

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

或者需要Person類的普通字段,或者動態驗證。

+0

我原以爲這樣可以,因爲我使得定義更強而不弱。 – vdhant 2008-11-13 03:25:06

0

這個怎麼樣:

public interface IFieldNormalItem : IFieldSimpleItem 
{ } 

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

現在你可以有Person<IFieldSimpleItem>(相當於Person)或Person<IFieldNormalItem>(相當於Bose)。

+0

爲了創建一個通用的方法,是否使類通用? – 2008-11-12 12:55:36

+0

我認爲這是一個「這是一個擁有簡單領域的人」vs「這是一個擁有普通領域的人」的案例。在這種情況下,你實際上是將這個人區分開來,並使這個類通用是可以接受的。 – tvanfosson 2008-11-12 13:41:11

+0

不幸的是yapiskan是正確的。在這種情況下,我不能讓整個班級成爲通用的(即Person ),因爲我需要做一些演員。 – vdhant 2008-11-13 03:19:19

0

下面的代碼足以覆蓋。類型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); 
    } 
} 
+0

但是,這隻會強制至少使用創建的IFieldSimpleItem。問題是如果某些東西從Bose繼承而來,並且使用返回IFieldSimpleItem實現的東西來覆蓋該創建,那麼它將會出錯,因爲bose需要它至少是IFieldNormalItem。 – vdhant 2008-11-13 03:24:13

+1

我把你的問題弄錯了。順便說一句,我更新了我的答案。 – 2008-11-13 13:10:47

1

看來你不能改變方法的定義,但你可以讓你的類通用,而不是創建方法?

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); 
    } 
} 
+0

不幸的是我不能這樣做。我剛剛花了幾年從我的代碼中刪除了泛型的風格。在這種情況下,我不能讓整個班級成爲通用的(即人),因爲我必須做一些演員。 – vdhant 2008-11-13 03:21:31

0

最簡單的例子是,這打破了多態性。如果你有個人,其中一個或一個以上這些項目的是百色類型的集合,這會盡快,因爲它擊中玻色崩潰。

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

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

改變通用約束改變,如果你要覆蓋一個虛擬這是不允許的方法簽名。

我想你可能需要創建方法分成單獨的類:

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; } 
} 
2

這是不允許的,因爲它違反了里氏替換原則。

比方說,你有另一個接口:

public interface IFieldSuperItem : IFieldSimpleItem 

然後你可以這樣做

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

在第二行中的呼叫,而在人創建的定義兼容,但顯然不兼容在Boss中定義(只適用於IFieldNormalItem及其子類)。