静态抽象方法的替代方法是什么?
我在尝试找出如何解决问题而无法在抽象类或接口中使用静态方法时遇到一些问题。考虑以下代码。我有很多继承自 AbsWizard 的 Wizard。每个向导都有一个 GetMagic(stringpell) 方法,该方法仅返回某些魔法词的魔法,但特定类型向导的所有实例都会响应同一组魔法词。
public abstract class AbsWizard
{
public abstract Magic GetMagic(String magicword);
public abstract string[] GetAvalibleSpells();
}
public class WhiteWizard : AbsWizard
{
public override Magic GetMagic(string magicword)
{
//returns some magic based on the magic word
}
public override string[] GetAvalibleSpells()
{
string[] spells = {"booblah","zoombar"};
return spells;
}
}
public class BlackWizard : AbsWizard
{
public override Magic GetMagic(string magicword)
{
//returns some magic based on the magic word
}
public override string[] GetAvalibleSpells()
{
string[] spells = { "zoogle", "xclondon" };
return spells;
}
}
我希望用户能够首先选择向导的类型,然后显示该类型的向导可以施展的法术列表。然后,当他们选择咒语时,程序将找到所选类型的所有(如果有的话)现有巫师,并让他们施展所选咒语。特定类型的所有巫师将始终具有相同的可用法术,并且我需要一种方法来确定特定类型的巫师可以施放的法术,而无需实际访问所选巫师类型的实例。
此外,我不想依赖于可能的向导类型或咒语的单独列表。相反,我宁愿通过 GetAvalibleSpells() 和反射来推断一切。例如我计划施展魔法如下:
public static void CastMagic()
{
Type[] types = System.Reflection.Assembly.GetExecutingAssembly().GetTypes();
List<Type> wizardTypes = new List<Type>();
List<string> avalibleSpells = new List<string>();
Type selectedWizardType;
string selectedSpell;
foreach (Type t in types)
{
if (typeof(AbsWizard).IsAssignableFrom(t))
{
wizardTypes.Add(t);
}
}
//Allow user to pick a wizard type (assign a value to selectedWizardType)
//find the spells the selected type of wizard can cast (populate availibleSpells)
//Alow user to pick the spell (assign a value to selectedSpell)
//Find all instances, if any exsist, of wizards of type selectedWizardType and call GetMagic(selectedSpell);
}
I'm having some problems trying to figure out how to solve a problem without being able to have static method in an abstract class or interface. Consider the following code. I have many Wizards that inherit from AbsWizard. Each wizard has a method GetMagic(string spell) that only returns magic for certain magic words, yet all instances of a specific type of wizard respond to the same set of magic words.
public abstract class AbsWizard
{
public abstract Magic GetMagic(String magicword);
public abstract string[] GetAvalibleSpells();
}
public class WhiteWizard : AbsWizard
{
public override Magic GetMagic(string magicword)
{
//returns some magic based on the magic word
}
public override string[] GetAvalibleSpells()
{
string[] spells = {"booblah","zoombar"};
return spells;
}
}
public class BlackWizard : AbsWizard
{
public override Magic GetMagic(string magicword)
{
//returns some magic based on the magic word
}
public override string[] GetAvalibleSpells()
{
string[] spells = { "zoogle", "xclondon" };
return spells;
}
}
I want the user to be able to first choose the type of wizard, and then be presented with a list of the spells that type of wizard can cast. Then when they choose a spell the program will find all, if any, existing wizards of the selected type and have them cast the selected spell. All wizards of a specific type will always have the same available spells, and I need a way to determine the spells a specific type of wizard can cast with out actually having access to an instance of the selected type of wizard.
In addition I don't want to have to depend on a separate list of possible wizard types or spells. Instead I would rather just infer everything through GetAvalibleSpells() and reflection. For example I plan to cast magic as follows:
public static void CastMagic()
{
Type[] types = System.Reflection.Assembly.GetExecutingAssembly().GetTypes();
List<Type> wizardTypes = new List<Type>();
List<string> avalibleSpells = new List<string>();
Type selectedWizardType;
string selectedSpell;
foreach (Type t in types)
{
if (typeof(AbsWizard).IsAssignableFrom(t))
{
wizardTypes.Add(t);
}
}
//Allow user to pick a wizard type (assign a value to selectedWizardType)
//find the spells the selected type of wizard can cast (populate availibleSpells)
//Alow user to pick the spell (assign a value to selectedSpell)
//Find all instances, if any exsist, of wizards of type selectedWizardType and call GetMagic(selectedSpell);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
我认为这是非常糟糕的风格。您编写了代码,因此您应该知道其中有哪些向导类。通过反射运行所有类型并检查它们是否派生自 AbsWizard 的方式非常糟糕(而且很慢!)。
I think this is very bad style. You write the code, so you should know what wizard-classes you have in there. It's very bad style (and slow!) to run through all types via reflection and check if they derive from AbsWizard.
托管扩展性框架(可通过 .NET-4.0 之前版本的 codeplex 或内置 .NET 4.0 获得)在 System.ComponentModel.Composition 命名空间)就是为此构建的。假设您有一个服务,它可以要求用户选择一个向导,然后创建它。它使用向导提供者来创建向导,并且需要知道提供者创建的向导的名称和可用咒语(元数据)。您可能会使用如下接口:
向导创建服务导入可用的向导提供程序,通过某种机制(在您的情况下为用户反馈)选择一个提供程序,并使用该提供程序来创建向导。
然后,您可以创建并导出任意数量的带有咒语的向导提供者,并且创建服务将能够找到它们:
当然,您还需要实现向导。
为了保持简洁,此代码使用自定义
NameAttribute
和SpellsAttribute
作为比ExportMetadataAttribute
:The Managed Extensibility Framework (available through codeplex for pre-.NET-4.0, or built-in .NET 4.0 in the System.ComponentModel.Composition namespace) was built for this. Say you have a service that can ask a user to select a wizard and then create it. It uses a wizard provider to create the wizards, and needs to know the name and available spells (metadata) for the wizards that a provider creates. You might use interfaces like these:
The wizard creation service imports the available wizard providers, selects one through some mechanism (user feedback in your case), and uses the provider to create the wizard.
You can then create and export an arbitrary number of wizard providers with spells, and the creation service will be able to find them:
Of course you'll need to implement the wizards as well.
To keep things clean, this code uses a custom
NameAttribute
andSpellsAttribute
as a much cleaner form of exporting metadata thanExportMetadataAttribute
:添加另一个间接级别。
GetAvailableSpells
方法并不是真正的实例方法,因为它对于所有实例都是相同的。正如您所指出的,您不能拥有抽象静态方法,因此请将特定于类型的内容移至基于实例的类工厂中。在下面的示例中,AvailableSpells
是MagicSchool
抽象类的一个方法,该抽象类具有具体子类BlackMagic
、WhiteMagic
Wizard
也有子类型,但每个Wizard
都可以返回它所属的MagicSchool
,为您提供类型安全的,与类型无关的方式来找出任何给定Wizard
对象的咒语,无需单独的表或代码重复。Add another level of indirection. The
GetAvailableSpells
method isn't really an instance method, since it's the same for all instances. As you pointed you, you can't have an abstract static method, so instead move the type-specific stuff into an instance-based class factory. In the example below,AvailableSpells
is a method of theMagicSchool
abstract class, which has concrete subclassesBlackMagic
,WhiteMagic
, etc. TheWizard
also has sub-types, but everyWizard
can return theMagicSchool
that it belongs to, giving you a type-safe, type-independent way to find out what the spells for any givenWizard
object are without separate tables or code duplication.首先,您应该真正考虑是否不能改变不使用巫师实例来发现其可用咒语的规则。我发现 原型模式 对于这类事情实际上非常有用。
但是,如果您确实无法做到这一点,您可以使用嵌套类和反射来发现特定具体 AbsWizard 派生体可以施放的可用咒语。这是一个例子:
有很多方法可以改进上面的代码。
首先,您可以定义自己的自定义属性,您可以在每个向导的嵌套类上标记该属性以识别拼写词典。
其次,使用字符串来定义可用的咒语可能最终会受到一些限制。您可能会发现定义所有可用咒语的全局静态列表更容易(作为某种类,我们将其称为
Spell
)。然后,您可以根据此列表而不是字符串来定义向导的可用咒语。第三,考虑为这个东西创建一个外部配置,而不是嵌入的嵌套类。它更灵活并且可能更容易维护。然而,编写如下代码可能会很好:
最后,考虑为每个向导派生创建一个静态字典来管理可用咒语列表,以便您可以避免多次执行反射(这是昂贵的)。
First, you should really consider whether you can't bend the rules of not using instances of Wizards to discover their available spells. I find that the prototype pattern can actually be quite useful for this sort of thing.
However, if you really can't do that, you can use nested classes and reflection to discover the available spells that a particular concrete AbsWizard-derivative can cast. Here's an example:
There are many ways to improve the above code.
First, you can define your own custom attribute that you can tag on the nested classes of each wizard to identify the spell lexicon.
Second, using strings to define the available spells may end up being a bit limiting. You may find it easier to define a global static list of all available spells (as some kind of class, let's call it
Spell
). You could then define the available spells of the wizard based off this list, rather than strings.Third, consider creating an external configuration for this thing rather than embedded, nested classes. It's more flexible and possibly easier to maintain. However, it can be nice to write code like:
Finally, consider creating a static dictionary for each Wizard-derivative that manages the list of available spells so that you can avoid performing reflection (which is expensive) more than once.
由于咒语与向导的类型相关,因此我将通过属性来执行此操作:
然后您声明一个向导类型,如下所示:
然后从程序集中加载向导类型的类可以检查每个向导类是否具有此属性,如果是这样,则使可用的类型(或引发异常)。
Since spells are tied to the type of the wizard, I'd do this through attributes:
Then you declare a wizard type like this:
Then the class that loads wizard types from the assembly can check each wizard class has this attribute and if so makes the type available (or throws an exception).
这能满足您的需要吗?根据需要将每种类型的向导添加到工厂。向导永远不会在您的库之外实例化,而只会在库内实例化。对于图书馆外的人来说,要获得巫师,他们会致电工厂以获取支持给定咒语的巫师。工厂自己设立。只需向工厂注册每个新向导即可。
Does this do what you need? Add each type of wizard to the factory as needed. Wizards will never be instantiated outside of your library, only inside it. For someone outside your library to get a wizard, they make a call to the factory to get those wizards that support a given spell. The factory sets itself up. Just register each new wizard with the factory.
使用工厂类来实例化您的向导。工厂有一个
方法可以让你确定巫师可以施展哪些法术。工厂还调用相同的方法来构造一个新的向导实例并设置其法术集。
Use a factory class to instantiate your wizards. The factory has a
method that allows you to determine which spells a wizard can cast. The factory also calls this same method to construct a new wizard instance and set its spell set.