有静态抽象方法的替代方法是什么?

Eri*_*tas 5 c# static abstract-class interface

我试图找出如何在抽象类或接口中无法使用静态方法来解决问题时遇到一些问题.请考虑以下代码.我有许多从AbsWizard继承的向导.每个向导都有一个方法GetMagic(字符串拼写),它只返回某些魔术字的魔法,但特定类型的向导的所有实例都响应同一组魔术字.

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我希望用户能够首先选择向导的类型,然后显示向导可以强制转换的法术列表.然后当他们选择一个咒语时,程序将找到所有类型的现有向导(如果有的话)并让他们施放所选的咒语.所有特定类型的向导都将具有相同的可用法术,并且我需要一种方法来确定特定类型的向导可以强制转换的法术,而实际上可以访问所选类型的向导的实例.

此外,我不想依赖于可能的向导类型或法术的单独列表.相反,我宁愿通过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);
    }
Run Code Online (Sandbox Code Playgroud)

Sam*_*ell 1

托管扩展性框架(可通过 .NET-4.0 之前版本的 codeplex 或System.ComponentModel.Composition命名空间中的内置 .NET 4.0 获得)就是为此构建的。假设您有一项服务可以要求用户选择一个向导,然后创建它。它使用向导提供者来创建向导,并且需要知道提供者创建的向导的名称和可用咒语(元数据)。您可能会使用如下接口:

namespace Wizardry
{
    using System.Collections.Generic;

    public interface IWizardProvider
    {
        IWizard CreateWizard();
    }

    public interface IWizard
    {
        IMagic GetMagic(string magicWord);
    }

    public interface IWizardProviderMetadata
    {
        string Name { get; }

        IEnumerable<string> Spells { get; }
    }
}
Run Code Online (Sandbox Code Playgroud)

向导创建服务导入可用的向导提供程序,通过某种机制(在您的情况下为用户反馈)选择一个提供程序,并使用该提供程序来创建向导。

namespace Wizardry
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.Linq;

    public class UserWizardCreationService
    {
        [Import]
        private IEnumerable<Lazy<IWizardProvider, IWizardProviderMetadata>> WizardProviders { get; set; }

        public IWizard CreateWizard()
        {
            IWizard wizard = null;
            Lazy<IWizardProvider, IWizardProviderMetadata> lazyWizardProvider = null;
            IWizardProvider wizardProvider = null;

            // example 1: get a provider that can create a "White Wizard"
            lazyWizardProvider = WizardProviders.FirstOrDefault(provider => provider.Metadata.Name == "White Wizard");
            if (lazyWizardProvider != null)
                wizardProvider = lazyWizardProvider.Value;

            // example 2: get a provider that can create a wizard that can cast the "booblah" spell
            lazyWizardProvider = WizardProviders.FirstOrDefault(provider => provider.Metadata.Spells.Contains("booblah"));
            if (lazyWizardProvider != null)
                wizardProvider = lazyWizardProvider.Value;

            // finally, for whatever wizard provider we have, use it to create a wizard
            if (wizardProvider != null)
                wizard = wizardProvider.CreateWizard();

            return wizard;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以创建并导出任意数量的带有咒语的向导提供者,并且创建服务将能够找到它们:

namespace Wizardry
{
    using System.ComponentModel.Composition;

    [Export(typeof(IWizardProvider))]
    [Name("White Wizard")]
    [Spells("booblah", "zoombar")]
    public class WhiteWizardProvider : IWizardProvider
    {
        public IWizard CreateWizard()
        {
            return new WhiteWizard();
        }
    }

    [Export(typeof(IWizardProvider))]
    [Name("White Wizard")]
    [Spells("zoogle", "xclondon")]
    public class BlackWizardProvider : IWizardProvider
    {
        public IWizard CreateWizard()
        {
            return new BlackWizard();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,您还需要实施向导。

namespace Wizardry
{
    using System;

    public class WhiteWizard : IWizard
    {
        public IMagic GetMagic(string magicWord)
        {
            throw new NotImplementedException();
        }
    }

    public class BlackWizard : IWizard
    {
        public IMagic GetMagic(string magicWord)
        {
            throw new NotImplementedException();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

为了保持简洁,此代码使用自定义NameAttributeSpellsAttribute比以下形式更简洁的导出元数据形式ExportMetadataAttribute

namespace Wizardry
{
    using System;

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
    public abstract class MultipleBaseMetadataAttribute : Attribute
    {
    }

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
    public abstract class SingletonBaseMetadataAttribute : Attribute
    {
    }

    public sealed class NameAttribute : SingletonBaseMetadataAttribute
    {
        public NameAttribute(string value) { this.Name = value; }
        public string Name { get; private set; }
    }

    public sealed class SpellsAttribute : MultipleBaseMetadataAttribute
    {
        public SpellsAttribute(params string[] value) { this.Spells = value; }
        public string[] Spells { get; private set; }
    }
}
Run Code Online (Sandbox Code Playgroud)