有没有一种巧妙的方法来指定一个类必须包含一个工厂方法,该方法返回与覆盖抽象方法的类相同类型的对象? (编辑: 或者正如 Johnathon Sullinger 更有说服力的说法,[...] 让基类强制子类实现返回子类本身实例的方法,并且不允许返回从基类继承的任何其他类型的实例。)
例如,如果我有两个类,SimpleFoo : BaseFoo
和 FancyFoo : BaseFoo
,我可以定义一个抽象工厂方法 public TFoo WithSomeProp( SomeProp prop)
其中 TFoo
是一个类型参数,由抽象方法定义以某种方式固定到覆盖它的特定类?
我希望编译时能保证这两者之一
SomeFoo : BaseFoo
中的具体WithSomeProp
方法定义将只能生成SomeFoo
。如果静态抽象方法定义是合法的,也许以下(伪语法)方法扩展最能表达这种需要:public static abstract TFoo WithSomeProp<TFoo>(this TFoo source, SomeProp prop) where TFoo : BaseFoo;
我认为这在 C# 中是不可能的。
或至少以某种方式在抽象方法中参数化返回类型,例如
public abstract TFoo WithSomeProp<TFoo>(SomeProp prop) where TFoo : BaseFoo;
这不会阻止
FancyFoo.WithSomeProp
返回SimpleFoo
,但是可以。这个抽象方法本身似乎可行,但我的具体定义却失败了:
public override SimpleFoo WithSomeProp(SomeProp prop) { return new SimpleFoo(this.SomeOtherProp, ..., prop); }
警告
no suitable method found to override
在我看来,在抽象方法中指定类型参数不允许在这些定义的覆盖中修复它们,而是指定“应该存在具有类型参数的方法”。
现在我只有 public abstract BaseFoo WithSomeProp(SomeProp prop);
。
最佳答案
这听起来像你想要做的,让一个基类强制一个子类实现一个返回子类本身实例的方法,并且不允许返回从基类继承的任何其他类型的实例类(class)。不幸的是,据我所知,这不是你能做的。
然而,您可以强制子类指定它对基类的类型,这样基类就可以强制返回值必须是子类指定的类型。
例如,给定一个名为 BaseFactory
的基类, 和 BaseFactory<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
代表自己。这意味着 child 只能返回自己。
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
一个 protected 方法,以便子类只能看到它。然后,您在基类上创建一个可以进行类型检查的公共(public)方法。
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();
}
}
不确定这是否至少能满足您的需求,但我认为这可能是您可以获得的最接近的东西。
关于c# - 具有固定类型参数的抽象工厂方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39711272/