2016-09-26 61 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

很高兴你能工作:) –