从子类调用(静态)方法

3
我创建了一个抽象类,叫做FooBarizable,它是FooBar这两个类(在实践中可能更多)的父类。现在,我有一个FooBarizableManager来管理FooBar类,具体取决于它的类型。从这个FroobarizableManager中,我想调用getFooBarizables()方法。让我们来看看它的结构:

FooBarizable.cs:

public abstract class FooBarizable{

    public string Name { get; set; }
    public static IEnumerable<FooBarizable> GetFooBars(){
        throw new NotImplementedException();
    }
}

Foo.cs:

public class Foo : FooBarizable{

    public static IEnumerable<FooBarizable> GetFooBars(){
        return API.getFoos();
    }
}

Bar.cs:

public class Bar : FooBarizable{

    public static IEnumerable<FooBarizable> GetFooBars(){
        return API.getBars();
    }
}

FooBarizableManager.cs:

public class FooBarizableManager {

    private Type type;
    public FooBarizableManager(Type _t){
        this.type = _t;
    }

    public void showFooBarizables(){

        MethodInfo method = type.GetMethod("GetFooBars");

        IEnumerable<FooBarizable> FooBars = (IEnumerable<FooBarizable>)method.invoke(null, null);

        show(FooBars);
    }

    ...

}

因此,我的问题是我想使用类型从管理器获取对象集合,但强制子类实现 " getFooBars()" 方法。
我面临的问题如下:
1. .Net不允许定义静态抽象方法,因此我无法创建 "public static abstract IEnumerable < FooBarizable > GetFooBars()" 并强制子类实现它。
2. 实现的方式不强制子类实现该方法,但我尝试至少抛出一个 "NotImplementedException"。问题在于当我在管理器中调用 "MethodInfo method = type.GetMethod("GetFooBars");" 时,如果子类没有实现该方法,则 "method" 为null,并且调用 "NullPointerException"。
3. 我已经尝试创建一个实例方法而不是静态方法,它解决了实施问题,因为子类必须实现它,但对我来说似乎创建一个不必要的实例来调用一个方法不正确。
那么,有什么方法可以强制子类实现 "getFooBars()" 方法吗? 如果没有,则如何抛出 "NotImplementedException" 而不是 "NullPointerException"?

接口的用例? - Barry O'Kane
GetFooBars() 可以调用抽象实例方法(以某种方式将实例传递给它),这将需要在 FooBar 中实现它。 - Sinatr
@Sinatr 我已经尝试过了。请查看我遇到的问题的第三点。 - Jose
6个回答

5
有没有办法强制子类实现getFooBar()方法?
对于静态方法来说,没有这样的解决方案。 静态方法与特定类绑定,因此它们不能被继承,也不能是抽象或虚拟的。
如果您想使该方法多态化,它需要成为一个实例方法。
我如何抛出NotImplementedException而不是NullPointerException
您得到该异常的结果是因为该类型没有GetFooBars 方法,因此methodnull。 所以您可以先检查是否为空。
public void showFooBarizables(){

    MethodInfo method = type.GetMethod("GetFooBars");

    if(method == null)
        throw new NotImplementedException();

    IEnumerable<FooBarizable> FooBars = (IEnumerable<FooBarizable>)method.invoke(null, null);

    show(FooBars);
}

但是抛出异常有点误导人,因为对于调用者来说,似乎是 showFooBarizables 方法没有实现,而不是潜在的 GetFooBars。既然这些方法看起来像是工厂方法,也许你需要为每种类型创建一个工厂呢?这似乎是在试图使用泛型代替重载。工厂方法通常不是泛型的,因为它们必须针对每种类型有不同的逻辑。你可以创建一个包含公共代码的“基础”工厂,然后为每种特定类型子类化该工厂。

4
当然,.NET不允许你编写虚拟静态方法 :)
虚拟方法的整个意义在于,当你在类型为“Derived”的实例上调用“Base.DoSomething”方法时,执行的是“Derived.DoSomething”中的代码。但这意味着你需要知道实例的实际运行时类型,以便知道真正应该执行哪个方法。
一个典型的替代方案是使用某种工厂接口。也就是说,你将获取相关实例类型的提供者实例,而不是查询“Foo.GetFooBars”或“Bar.GetFooBars”,例如“Fooizer.GetFooBars”和“Barizer.GetFooBars”,其中“Fooizer”和“Barizer”都实现了“IFooBarProvider”。因为“GetFooBars”不适合“FooBarizable”接口 - 因为它不属于那里。面向对象设计、职责、替换原则等等。
然后,如果你需要在代码中强制实现(例如确保有人没有忘记实现正确的提供者),你可以在你的类型上创建一个抽象实例方法或属性。
public abstract class FooBarizable
{
  protected abstract IFooBarProvider GetDefaultProvider();
}

根据您要完成的具体任务,将这些类变为通用类可能是有意义的。或者不需要 :)


我会查找所有这些概念并尝试一下,谢谢你的回答。 - Jose
好的,我理解的是:我需要创建一个提供者工厂,然后获取每个Foobarizable类的相应提供者实例,这样就解决了之前没有看到的责任问题。然后,我仍然有同样的问题,如果我没有Foo/Bar的实例,我无法获取Foo/Bar的DefaultProvider,因为getDefaultProvider不是静态的。那么,如何在考虑FooBarManager中唯一知道的变量类型的情况下获取具体提供者的实例?如果我在任何地方理解错误,请纠正我。 - Jose
1
@Kirenenko 两种主要的方法是注册和反射。注册很简单 - 只需有一个静态类,其中包含基于类型的所有提供程序的字典。反射意味着类似于“查找实现IFooBarProvider<Foo>的所有类型”。最后,您可能会发现没有理由拥有一个通用解决方案,可以在编译时不知道类型的情况下工作 - 您越多地处理依赖注入和显式类型,就会发现大多数事情在编译时可以静态地知道。 - Luaan
1
@Kirenenko 有时候看待问题的角度不同,会有意想不到的收获。例如,使用推送方法而不是拉取方法,反之亦然。很多时候,一个复杂的问题变得非常简单。 - Luaan
我已经采用了这种方法,传递提供程序的实例而不是类型。 - Jose

2

你不能强制子类定义静态方法。正如你所指出的,抽象方法不能是静态的,并且接口只能使用实例。我认为你试图将太多东西放入单个类中。看起来你在尝试创建一些“弗兰肯工厂”。相反,只需分离工厂功能和抽象父对象。

public abstract class FooBarizable {
    public string Name { get; set; }
}

工厂模式示例:

public static class FooBarizableFactory {

    public static IEnumerable<FooBarizable> GetFooBars(Type type){
        var parentType = typeof(FooBarizable);          
        if (!parentType.IsAssignableFrom(type))
            throw new ArgumentException("Not a FooBarizable");                  

        switch(type.Name) {
            case "Foo":
                return new List<Foo>() { new Foo () };

            case "Bar":
                return new List<Bar>() { new Bar() };

            default:
                throw new ArgumentException("Not a known FooBarizable");
        }
    }
}

使用方法:

var fooBars = FooBarizableFactory.GetFooBars(typeof(Foo));

这里有一个关于Idea的演示。


看起来工厂是正确的选择,正如你们中的许多人所指出的那样。感谢Franken-coder :) - Jose
但是...我认为switch-case与泛型不搭配,如果我错了请纠正我。 - Jose
@Kirenenko 注意它正在使用 type.Name(一个字符串)进行切换。可能有更少脆弱的切换类型的方法,但这是一个概念验证。 - ryanyuyu
这不是关于类型名称的问题,而更多地涉及到如果我向方案中添加一个新的FooBarizable类,我只想做: FooBarizable部分,而不是搜索我的代码中包含它的每个开关情况。 - Jose
1
@Kirenenko 工厂模式的思想是将 switch 语句放在唯一的位置。如果你添加了一个新类,你就必须在某个地方实现这个逻辑。而使用工厂模式,你知道去哪里实现它。不需要切换逻辑的任何其他内容都可以放在类本身(或父类)中。 - ryanyuyu

1

.Net不允许定义静态抽象方法。

因为C#编译器将静态方法作为抽象和密封。所以你不能只将其定义为抽象或密封。

问题在于当我在管理器中调用MethodInfo method = type.GetMethod("GetFooBars");时,如果子类没有实现该方法,method将为空,并且会调用NullPointerException。

我说过静态是抽象和密封的。因此,由于它是密封的,派生类将没有GetFooBars方法。

您可以使用BindingFlags.FlattenHierarchy标志。这样它也会检查基类的受保护和公共静态类。如果在派生类中未实现,则会检查基类。因此,在您的情况下,如果派生类没有此方法,则将调用基类GetFooBars


0

无论是通过任何形式的继承或者多态,都没有方法可以强制实现静态方法,但是一个潜在的解决方法是为 FooBarizable 实现一个或多个扩展方法,这样任何继承它的类将有权访问该扩展方法。


0

静态方法与类的对象(实例)无关,它始终与类本身相关。

强制实现方法的一种方式是使用接口。另一种方式,这也是我认为您想要的,因为此方法在不同实例中具有不同的行为,那就是使用抽象方法。

public abstract class AbstractClass 
{
    public abstract int MustIMplementThis(string param1);
}

public class ChildClass : AbstractClass
{

    public override int MustIMplementThis(string param1)
    {
        throw new NotImplementedException();
    }
}

所有从AbstractClass继承的类都必须实现父类中列出的方法。

我明白,但由于这些方法将具有不同的行为,您必须拥有一个实例。 - Ralph
目前我正在进行这项工作,但是没有使用实例,只是没有强制执行。 - Jose

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接