2016-09-26 42 views
1

有沒有指定一個類必須包含返回相同類型的對象,因爲它覆蓋抽象方法的類工廠方法一種巧妙的方法? (編輯:或者正如喬納森 - 薩林格更雄辯地指出的,[...]有一個基類強制子類實現返回子類本身的實例的方法,而不是讓任何歸國的一個實例其它類型的從基類繼承。抽象工廠方法固定類型參數

例如,如果我有兩個類,SimpleFoo : BaseFooFancyFoo : BaseFoo和,我可以定義一個抽象工廠方法public TFoo WithSomeProp(SomeProp prop)其中TFoo是某種方式由固定的類型參數抽象方法定義到覆蓋它的特定類?

我不得不編譯時間保證的希望,無論是

  1. SomeFoo : BaseFoo具體WithSomeProp方法定義將只能夠生產SomeFoo秒。如果靜態抽象方法的定義是合法的,也許以下(僞語法)方法擴展最能表達這方面的需求:

    public static abstract TFoo WithSomeProp<TFoo>(this TFoo source, SomeProp prop) 
        where TFoo : BaseFoo; 
    

    我不認爲這是可能在C#中。

  2. 或至少一些方式來參數返回類型在一個抽象方法,例如

    public abstract TFoo WithSomeProp<TFoo>(SomeProp prop) 
        where TFoo : BaseFoo; 
    

    這不會阻止FancyFoo.WithSomeProp從返回SimpleFoo S,但確定。

    這個抽象方法本身似乎工作,但後來我的具體定義失敗:

    public override SimpleFoo WithSomeProp(SomeProp prop) 
    { 
        return new SimpleFoo(this.SomeOtherProp, ..., prop); 
    } 
    

    與警告

    發現覆蓋

    這似乎沒有合適的方法我在一個抽象的方法指定類型的參數不允許在這些定義的覆蓋修復它們,而是規定「應該存在一個帶有類型參數的方法「。

現在我只需要public abstract BaseFoo WithSomeProp(SomeProp prop);

+0

我可以看到這樣的例子使用,所以我可以試着去了解你將如何使用這個API。例如,如果允許靜態摘要,你將如何使用它?這不過是一個靜態工廠方法來創建和配置子實例嗎? –

回答

4

這聽起來像你想做的事,是有一個基類強制子類實現返回子類本身的實例的方法,而不是允許返回從繼承的任何其他類型的實例基類。不幸的是,據我所知,這不是你可以做的事情。

但是,您可以強制子類來指定它的類型是基類,從而使基類就可以強制執行的返回值必須是子類中指定的類型。

例如,給定一個名爲BaseFactoryBaseFactory<T>的基類,我們可以創建一個抽象類,它要求子類向父類指定創建方法返回的類型。我們包括一個BaseFactory類,因此我們可以將T限制爲BaseFactory的子類。

編輯

我會離開原來答案在下面的事件,它可以幫助,但經過一番思考,我想我得給你一個更好的解決方案。

您仍然需要基類來獲取一個通用參數,該參數定義子類型是什麼。但現在的區別是基類有一個靜態創建方法,而不是實例方法。您可以使用此創建方法創建子類的新實例,並可選擇在返回之前調用回調來配置新實例上的屬性值。

public abstract class BaseFactory { } 

public abstract class BaseFactory<TImpl> : BaseFactory where TImpl : BaseFactory, new() 
{ 
    public static TImpl Create(Action<TImpl> itemConfiguration = null) 
    { 
     var child = new TImpl(); 
     itemConfiguration?.Invoke(child); 
     return child; 
    } 
} 

然後,您只需正常創建您的子類,而不必擔心重寫任何方法。

public class Foo : BaseFactory<Foo> 
{ 
    public bool IsCompleted { get; set; } 
    public int Percentage { get; set; } 
    public string Data { get; set; } 
} 

public class Bar : BaseFactory<Bar> 
{ 
    public string Username { get; set; } 
} 

然後你會使用工廠。

class Program 
{ 
    static void Main(string[] args) 
    { 
     // Both work 
     Bar bar1 = Bar.Create(); 
     Foo foo1 = Foo.Create(); 

     // Won't compile because of different Types. 
     Bar bar2 = Foo.Create(); 

     // Allows for configuring the properties 
     Bar bar3 = Bar.Create(instanceBar => instanceBar.Username = "Jane Done"); 
     Foo foo2 = Foo.Create(instanceFoo => 
     { 
      instanceFoo.IsCompleted = true; 
      instanceFoo.Percentage = 100; 
      instanceFoo.Data = "My work here is done."; 
     }); 
    } 

原來的答案

BaseFactory<T>將承擔一切創造TImpl一個新實例,並給它回來。現在

public abstract class BaseFactory { } 

public abstract class BaseFactory<TImpl> : BaseFactory where TImpl : BaseFactory 
{ 
    public abstract TImpl WithSomeProp(); 
} 

,你的子類可以被創建,並從BaseFactory<T>繼承,告訴的基類T代表本身。這意味着孩子只能迴歸自己。

public class Foo : BaseFactory<Foo> 
{ 
    public override Foo WithSomeProp() 
    { 
     return new Foo(); 
    } 
} 

public class Bar : BaseFactory<Bar> 
{ 
    public override Bar WithSomeProp() 
    { 
     return new Bar(); 
    } 
} 

那麼你可以使用它像:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var obj1 = new Bar(); 

     // Works 
     Bar obj2 = obj1.WithSomeProp(); 

     // Won't compile because obj1 returns Bar. 
     Foo obj3 = obj1.WithSomeProp(); 
    } 
} 

如果你真的想確保指定的一般是一樣的所屬類型,則可能反而讓WithSomeProp一個受保護的方法,所以兒童班只能看到它。然後,您可以在可以進行類型檢查的基類上創建一個公共方法。現在

public abstract class BaseFactory { } 

public abstract class BaseFactory<TImpl> : BaseFactory where TImpl : BaseFactory 
{ 
    protected abstract TImpl WithSomeProp(); 

    public TImpl Create() 
    { 
     Type myType = this.GetType(); 
     if (typeof(TImpl) != myType) 
     { 
      throw new InvalidOperationException($"{myType.Name} can not create instances of itself because the generic argument it provided to the factory is of a different Type."); 
     } 

     return this.WithSomeProp(); 
    } 
} 

public class Foo : BaseFactory<Foo> 
{ 
    protected override Foo WithSomeProp() 
    { 
     return new Foo(); 
    } 
} 

public class Bar : BaseFactory<Bar> 
{ 
    protected override Bar WithSomeProp() 
    { 
     return new Bar(); 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var obj1 = new Bar(); 

     // Works 
     Bar obj2 = obj1.Create(); 

     // Won't compile because obj1 returns Bar. 
     Foo obj3 = obj1.Create(); 
    } 
} 

,如果你創建一個通過不同類型的T一個子類,基礎類將捕捉到它並拋出異常。

// Throws exception when BaseFactory.Create() is called, even though this compiles fine. 
public class Bar : BaseFactory<Foo> 
{ 
    protected override Foo WithSomeProp() 
    { 
     return new Foo(); 
    } 
} 

不知道這是否得到你想要的東西,但我認爲這可能是你可以得到的最接近的東西。

+0

非常感謝。你在課堂上通過類型的方法是鼓舞人心的。其他一些偏差是我的屬性是隻讀的,而且我沒有無參數的構造函數(new()需要)。 –

1

受Johnathon Sullinger正確答案的啓發,這裏是我結束的代碼。 (我添加了一個主題。)

我傳遞了類型參數T以及類定義並約束了T : Base<T>

  • BaseHyperLink。CS:

    public abstract class BaseHyperLink<THyperLink> : Entity<int> 
        where THyperLink : BaseHyperLink<THyperLink> 
    { 
        protected BaseHyperLink(int? id, Uri hyperLink, ContentType contentType, DocumentType documentType) 
         : base(id) 
        { 
         this.HyperLink = hyperLink; 
         this.ContentType = contentType; 
         this.DocumentType = documentType; 
        } 
    
        public Uri HyperLink { get; } 
        public ContentType ContentType { get; } 
        public DocumentType DocumentType { get; } 
    
        public abstract THyperLink WithContentType(ContentType contentType); 
    } 
    
  • SharedHyperLink.cs:

    public sealed class SharedHyperLink : BaseHyperLink<SharedHyperLink> 
    { 
        public SharedHyperLink(int? id, Uri hyperLink, ContentType contentType, DocumentType documentType) 
         : base(id, hyperLink, contentType, documentType) 
        { 
        } 
    
        public override SharedHyperLink WithContentType(ContentType contentType) 
        { 
         return new SharedHyperLink(this.Id, contentType, this.DocumentType); 
        } 
    } 
    
  • MarkedHyperLink.cs:

    public sealed class MarkedHyperLink : BaseHyperLink<MarkedHyperLink> 
    { 
        public MarkedHyperLink(int? id, Uri hyperLink, ContentType contentType, DocumentType documentType, Mark mark) 
         : base(id, hyperLink, contentType, documentType) 
        { 
         this.Mark = mark; 
        } 
    
        public Mark Mark { get; } 
    
        public override MarkedHyperLink WithContentType(ContentType contentType) 
        { 
         return new MarkedHyperLink(this.Id, contentType, this.DocumentType, this.Mark); 
        } 
    } 
    
+0

很高興你能工作:) –