c# - 具有固定类型参数的抽象工厂方法

标签 c# types

有没有一种巧妙的方法来指定一个类必须包含一个工厂方法,该方法返回与覆盖抽象方法的类相同类型的对象? (编辑: 或者正如 Johnathon Sullinger 更有说服力的说法,[...] 让基类强制子类实现返回子类本身实例的方法,并且不允许返回从基类继承的任何其他类型的实例。)

例如,如果我有两个类,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,但是可以。

    这个抽象方法本身似乎可行,但我的具体定义却失败了:

    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/

相关文章:

c# - 使用真实世界的单位而不是类型

c# - 如何简化mysql中的更新查询?

c# - 响应式扩展 BufferWithPredicate

c# - 定义一个 C# 方法,它接受任何可以与方括号一起使用的对象

swift - 在 Swift 浮点值和 "X": Polloquin Exercise Notation 中创建类型

TypeScript 无法推断出正确的类型

c# - 如何查找 .NET 应用程序在 GC 中花费的时间百分比?

c# - 文本框绑定(bind)不起作用

c# - 至少应通过 asp.net mvc 中的自定义验证检查一个单选按钮

Haskell 无限类型和我的 FSM 函数