gpt4 book ai didi

c# - 静态定义派生类类型的选项

转载 作者:行者123 更新时间:2023-11-30 22:56:07 26 4
gpt4 key购买 nike

我正在开发一个框架,其中从框架的抽象类继承的类需要能够指定在调用 DoStuff() 时可以接受的选项的模式。

我从像这样的抽象 GetOptionsSchema() 方法开始:

public abstract class Widget
{
public abstract OptionsSchema GetOptionsSchema();
public abstract void DoStuff(Options options);
}

然后其他开发人员会通过创建自定义 Widget 类型来扩展我的框架:

public abstract class FooWidget: Widget
{
public overide DoStuff(Options options)
{
//Do some FooWidget stuff
}

public overide OptionsSchema GetOptionsSchema()
{
//Return options for FooWidget
}
}

这可行,但需要框架创建每个 Widget 类型的实例以确定它们接受的选项架构,即使它实际上不需要对这些类型中的任何一个执行 DoStuff()。

最终,我希望能够直接从 System.Type 确定特定 Widget 类型的选项架构。我会创建一个自定义的 OptionsSchema 属性,但是构建这些模式比在属性的构造函数中更有意义。它需要在一个方法中发生。

我已经看到其他框架通过创建一个自定义属性来解决类似的问题,该属性按名称标识静态方法或属性。例如 TestCaseSource NUnit 中的属性。

此选项可能如下所示:

public abstract class Widget
{
public abstract void DoStuff(Options options);
}

[OptionsSchemaSource(nameof(GetOptionsSchema))]
public abstract class FooWidget: Widget
{
public overide DoStuff(Options options)
{
//Do some FooWidget stuff
}

public static OptionSchema GetOptionsSchema()
{
//Return options for FooWidget
}
}

我喜欢 OptionsSchemaSource 属性如何使直接从 System.Type 获取选项模式成为可能,但这对于创建自定义 Widget 类型的其他开发人员来说似乎也不太容易发现。

使用抽象方法,另一个 Widget 开发人员知道他们必须覆盖 GetOptionSchema(),因为否则他们的代码将无法编译。对于 OptionsSchemaSource 属性,我能做的最好的事情就是希望人们阅读我的文档,并让框架在遇到没有 OptionsSchemaSource 属性的 Widget 时在运行时抛出异常。

对此有替代/更好/推荐的方法吗?

最佳答案

您几乎已经了解所有感兴趣的东西,可以判断什么是最佳方法。

如前所述,您不能在类型上定义静态接口(interface),因此无法确保新开发人员强制添加该属性。

因此,您确定的两个备选方案是我能想到的仅有的两个。现在,让我们比较一下利弊,并尝试磨砺它们。

属性

您可以减轻确保开发人员将属性放在具有有意义的错误消息的类上的痛苦。我会说你应该管理完全基于属性的类的发现,而不是继承。如果你用Attributes来管理一切,你就不需要继承Widget。这是一个优点,因为现在每个人都可以在需要时继承,并在需要时重新实现。

缺点是可发现性的实现会更复杂:您需要在启动时使用反射,获取 MethodInfo,检查方法是否具有正确的签名,在case 并根据需要调用对结果进行拆箱的方法。

想一想:您需要一个静态方法,因为您不需要实例化单个类型化的 Widget 实例,但实际上实例化一个新的 Widget 很可能不是什么大问题。

抽象类

好吧,你对你的开发人员实现了一个继承链,这可能是好的、必要的或完全可选的(你判断),但你会获得 self 记录的体验。

明显的缺点是在启动时您需要为您发现的每个派生类型实例化一个 Widget,但与程序集扫描和类型检查以及通​​过反射进行方法信息发现和方法调用相比,这很可能是小菜一碟。丑陋的?有点儿。效率低下?没那么多。它是您的最终用户看不到的代码。

恕我直言

我发现一个很好的权衡,在设计框架时,将一些“丑陋”的代码放在框架中,如果这意味着使用该库的每个单独实现都会有一点点好一点。

总而言之,如果您要设计一个灵活且易于发现的库,您应该期望开发人员至少阅读快速入门指南。如果他们能在 5 分钟内阅读一点信息(“扩展基类”或“添加一个或几个属性”)并且这一点为他们提供了发现小部件注册的各个方面的方向,我会好的:没有比这更好的了。

我的建议:我会选择抽象类路线,但要注意一点。我真的不喜欢强制执行基类。因此,我将在启动时根据接口(interface) IWidget 组织发现,其中包含 GetOptionsSchema 方法和使用小部件所需的一切(可能是 DoStuff 方法,但很可能是其他东西)。在启动时,您搜索非抽象接口(interface)的实现,然后就可以开始了。

如果,并且仅当您真正提前需要的唯一位是字符串或其他类似的简单类型,我将需要一个额外的属性。

[OptionsSchemaName("http://something")]
public class MyWidget : WidgetBase
{
public overide DoStuff(Options options)
{
//Do some FooWidget stuff
}

public static OptionSchema GetOptionsSchema()
{
//Return options for FooWidget
}
}

然后,您的类型发现基础架构可以搜索非抽象的 IWidget 并在启动时立即抛出有意义的错误,例如 MyWidget 类型缺少 OptionsSchemaName 属性。 IWidget 的每个实现都必须定义一个。有关信息,请参阅 http://mydocs

砰!成功了!

关于c# - 静态定义派生类类型的选项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54682448/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com