【问题标题】:Self-referenced generic parameter自引用的泛型参数
【发布时间】:2019-02-17 14:58:13
【问题描述】:

例如,我有以下课程:

1.

class MyClass1
{
    public MyClass1 Method()
    {
        ...
        return new MyClass1();
    }
}

class MyClass2
{
    public MyClass2 Method()
    {
        ...
        return new MyClass2();
    }
}

这些方法具有相同的主体,这就是我要提取代码并重用的原因。

2.

abstract class MyClass
{

    protected void Method()
    {
        ...
    }
}

class MyClass1 : MyClass
{

    public MyClass1 Method()
    {
        base.Method();
        return new MyClass1();
    }
}

class MyClass2 : MyClass
{
    public MyClass2 Method()
    {
        base.Method();
        return new MyClass2();
    }
}

但是,由于有很多这样的方法,最好将这些方法移到基类 MyClass 中:

3.

abstract class MyClass<T>: where T : MyClass<T>
{
    protected abstract T Create();

    public T Method()
    {
        ...
        return Create();
    }
}

class MyClass1 : MyClass<MyClass1>
{
    protected override MyClass1 Create() => new MyClass1();
}

class MyClass2 : MyClass<MyClass2>
{
    protected override MyClass2 Create() => new MyClass2();
}

它工作正常,但合同看起来太奇怪了。当然,我可以提取 IMyInterface 之类的内容并返回它而不是类。但是我必须保留原始合同,因为它也包含特定的方法。

更新:所以,奇怪的是粗体 - class MyClass1: MyClassMyClass1>

【问题讨论】:

标签: c# .net generics typing


【解决方案1】:

这是通常的所谓自类型问题(您有Method() 应该返回与调用它的对象相同的类型)。您的解决方案 #3 看起来很像 F 有界量化。但是,这是 C#,而不是 Java,所以我们可以使用扩展类做得更好。

您可以通过在T 上添加where T : MyClass 绑定来确保仅在MyClass 的子类上调用这些方法。

// Put all your shared methods in generic classes in here.
public static class MyMethods
{
    public static T Method<T>(this T x) where T : MyClass
    {
        ...
    }
}

您的类没有太大变化,只是根本不需要提及Method(或其他共享方法)。

public abstract class MyClass
{
    ...
}

public class MyClass1 : MyClass
{
    ...
}

public class MyClass2 : MyClass
{
    ...
}

【讨论】:

  • 这是c#:public static T Method&lt;T extends MyClass&gt;(this T x) 中的新功能吗?这是第一次看到extends关键字的使用...
  • @IronGeek 不,只是我忘记了 C# 的具体语法。移入where 子句。这一次,我还检查了所有编译。谢谢你让我诚实:)
  • 它只允许公共访问。 MyClass 的私有/受保护不可用。
  • @Serg046 完全正确。 protected 是这里真正的问题:这些不是真正的方法,因此您将永远无法覆盖它们。如果您打算在项目中包含MyClass 的所有子类,internal 可能会代替private
【解决方案2】:

是的,有一个只创建的方法看起来有点奇怪。

因为你有 2 个类 MyClass1 和 MyClass2,它们有各自不同的方法,并且只有基方法是常见的(你放在基类中)我认为你可以使用抽象工厂模式。

public class ClassFactory: IClassFactory
{
    public MyClass Create()
    {
         if (some condition)
              return new MyClass1;
         return new MyClass2;
    }
}

class MyClass
{
    protected string CommonLogic()
    {
         //common logic 
         return string;
    }

}

class MyClass1 : MyClass
{
    public object SpecificMethod()
    {
        CommonLogic();
        .....
    }

}

class MyClass2 : MyClass
{
    public object SpecificMethod2()
    {
        CommonLogic();
        .....
    }
}

在这种情况下,您将不会有重复的代码,并且您将拥有一些类,该类将负责创建您的类并知道何时以及哪个类返回。 + 您将在这里轻松使用 IoC。

希望我的回答对你有所帮助。

【讨论】:

  • 它有什么帮助?我的#2 和你的建议有什么区别?无论如何,感谢您的时间和尝试。
  • #2 中的逻辑和我的答案不太一样。您的问题仅在责任方面。您有 2 个由他们自己创建的类。在我的情况下,另一个类将决定如何创建,这意味着它将更加灵活。在顶层发送所有依赖项是一个很好的做法。现在如果你需要使用我的工厂,你需要添加构造函数依赖,然后创建你想要的。如果您需要更改创建类的逻辑,您只需创建另一个工厂实现并更改 IoC 容器的设置。
  • 或者您可以更新您将如何使用这种常用方法,我们会找到其他解决方案或保留您的解决方案。
  • 我需要 MyClass1/MyClass2 合同。他们有他们特定的方法。我无法用一份合同来描述它们。 Of course, I can extract something like IMyInterface and return it instead of the class. However I have to preserve the original contract because it contains specific methods as well.
  • 选角?不,谢谢。我的选项#2、#3 和你原来的建议更好。
【解决方案3】:

在一些罕见的情况下可能需要自引用类型约束,但我不相信这是其中之一。

您似乎想使用工厂(创建)模式并让这些工厂返回不同的具体类型。但同时你说这些具体类型都有一些共同点,由基类指定。

传统的方法是在接口中定义通用功能(如您所建议的 IMyInterface)并从 Create 方法返回。这将捕获具体类的多态方面。那么问题是如何捕获在具体类型中实现的其他方法。为此,可以简单地定义额外的接口来捕获由多个具体类实现的各种功能集群。

如果在您完成此操作后还剩下一点点和点点的功能,我会说通过强制转换来处理它们将是最简单的 - 在这一点上,该功能将是唯一的一个具体类。如果你想闭上眼睛飞行,你可以使用“动态”类型而不是投射。

另外,通常 Create 方法没有在对象实例中定义,换句话说,对象通常不是它们自己的工厂。通常它们是静态的或在单独的工厂类中。在当前情况下,一些反思有助于处理您有多个派生类型的事实。除了我在下面展示的方法之外,还有多种方法可以做到这一点。

所以……也许是这样的:

public interface ICommonFunctionality
{
    void SomethingThatEveryoneCanDo();
    // ... other common functionality
}

public interface IAdditionalFunctionality1
{
    void SomethingThatAFewCanDo();
    // ... other special functionality
}
public interface IAdditionalFunctionality2
{
    void SomethingThatOthersCanDo();
    // ... other special functionality
}

public class MyClass : ICommonFunctionality
{
    static public ICommonFunctionality Create(Type derivedType)
    {
        if (!typeof(ICommonFunctionality).IsAssignableFrom(derivedType)) { throw new ArgumentException(); }
        return derivedType.CreateInstance() as ICommonFunctionality;
    }

    virtual public void SomethingThatEveryoneCanDo() { /* ... */  }
}

public class MyClass1 : MyClass, IAdditionalFunctionality1
{
    public void SomethingThatAFewCanDo() { /* ... */ }
}

public class MyClass2 : MyClass, IAdditionalFunctionality1, IAdditionalFunctionality2
{
    public void SomethingThatAFewCanDo() { /* ... */ }
    public void SomethingThatOthersCanDo() { /* ... */ }
}

public class MyClass3 : MyClass, IAdditionalFunctionality2
{
    public void SomethingThatOthersCanDo() { /* ... */ }
}

public static class TypeHelpers
{
    public static object CreateInstance(this Type type, bool required = true)
    {
        System.Reflection.ConstructorInfo ctor = type.GetConstructor(Type.EmptyTypes);
        if (required && ctor == null) { throw new InvalidOperationException("Missing required constructor."); }
        return ctor?.Invoke(null);
    }
}

附:我已将基类方法设为虚拟,这几乎是可选的,具体取决于您的情况。

【讨论】:

  • 这个答案的术语很厚,实际代码很薄。对于 OP 的 MyClassMyClass1MyClass2Method,您将如何实现这一点?我看不出这只是对 OP 第三次尝试的部分描述。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-01-02
  • 1970-01-01
  • 2012-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多