选择一个子类而不知道它在C#中的名称

时间:2017-06-21 13:23:11

标签: c# inheritance plugins module subclass

编辑1:如果有人有更好的标题,请随时告诉我或自己编辑。
编辑2:感谢您的贡献,给出的答案几乎是我需要的一些调整,我感谢这里的小东西。今天真的学到了很多!

这里的小东西,我现在已经敲了一会儿 我想创建一个幻灯片,并希望Image对象本身具有逻辑。

程序应该能够设置所需的转换,或者只是随机的转换 我想用一般的东西和spezialize来创建一个Transition Superclass 它在子类中。所以我有Transitions.cs(当前没有Code代码)
而且没有派生类。我希望它能够添加一个.cs文件 扩展Transitions.cs而不更改任何其他代码以实现新的Transition。

我目前的代码看起来像这样,但我猜我的 描述比代码更有用

public class SlideImages : MonoBehaviour {

    Image image;
    Image nextImage;
    int tracker;

    private void Transition(int ID)
    {
        /*Something to choose a transition based on the ID
        *Transitions.cs is the superclass of all different transitions
        *f.e. Ken-Burns Effect, or scattered Transition which all extend from it
        */
    }

    ~SlideImages()
    {
        //TODO: Pop and Push
    }
}

我想到了一些与静态东西相关的东西来解决这个问题 看起来像这样,但我认为它不起作用

public class Transitions : MonoBehaviour {

    public static int TransitionID;
    protected static int SubclassCount;

    protected static void SetID()
    {
        TransitionID = Transitions.SubclassCount;
        Transitions.SubclassCount++;
    }
}

我确实调查过状态设计模式,但我不想实现它,因为我只需要选择一次并且短暂的状态。图像对象本身只有几秒钟的寿命。我不想做通常的if-nesting或只是将所有代码放在SlideImages.cs中。是否有任何良好的指导或对继承和这些东西非常深入的东西?

感谢所有输入。

2 个答案:

答案 0 :(得分:0)

您想要做的事情有两个直接的解决方案。您的基本问题是您希望能够为您的程序动态添加功能,但您不想知道为了使用它而添加的内容。最常用的方法是使用Actions而不是子类化。如果要添加其他转换,只需更新动作列表,如下所示:

public static class Transitions
{
    private static Action[] TransitionStrategies = new Action[]
    {
        () => { /* One way of performing a transition */ },
        () => { /* Another way of performing a transition */ },
        () => { /* Keep adding methods and calling them here for each transition type */ }
    }

    public static void PerformTransition(int? transitionIndex = null)
    {
        int effectiveIndex;

        // if the transition index is null, use a random one
        if (transitionIndex == null)
        {
            effectiveIndex = new Random().Next(0, TransitionStrategies.Length);
        }
        else
        {
            effectiveIndex = transitionIndex.Value;
        }

        // perform the transition
        TransitionStrategies[effectiveIndex]();

    }
}

上述方法很简单,但所有逻辑(或者至少是逻辑的引用取决于实现转换的实际工作的位置)都在一个地方。根据您添加的转换次数以及有多少开发人员触及此代码库,它还有可能变得非常混乱。它还要求有权访问完整代码库的人添加所有功能,并且每次添加新的转换时都需要重新编译。

从长远来看,更复杂但更易于维护和灵活的方法是使用模块(或插件用于我们的目的)。该方法中的每个转换都由共享模块或特定模块提供,并且是基本AbstractTransition类的子类或ITransition接口的实现,具体取决于您希望如何执行此操作。使用构建后任务将所有模块dll放在主程序可访问的单个目录中(获得权限的任何其他人都可以将转换模块dll放在那里)。当程序启动时,它会动态加载该目录中的所有dll(只要正确的dll位于该目录中,就不需要重新编译,只要正确的dll位于该目录中),并拉出实现该接口的所有类。这些接口实现中的每一个都被实例化并放入数据结构中,之后您可以使用与上述PerformTransition方法类似的策略来基于ID而不是索引执行随机的一个或一个。如果您愿意,我可以通过该结构的示例编辑此问题。

编辑:你还没有提出要求,但这是插件/模块的一个例子。

首先,创建一个项目来加载和运行转换。此示例将使用名为ModuleDemo的项目。给它一个这样的主要方法:

static void Main(string[] args)
{
    // create a list to hold the transitions we load
    List<AbstractTransition> transitions = new List<AbstractTransition>();

    // load each module we find in the modules directory
    foreach (string dllFilepath in Directory.EnumerateFiles("Modules", "*.dll"))
        // this should really read from the app config to get the module directory                
    {
        Assembly dllAssembly = Assembly.LoadFrom(dllFilepath);

        transitions.AddRange(dllAssembly.GetTypes()
            .Where(type => typeof(AbstractTransition).IsAssignableFrom(type))
            .Select(type => (AbstractTransition) Activator.CreateInstance(type)));
    }

    // show what's been loaded
    foreach (AbstractTransition transition in transitions)
    {
        Console.WriteLine("Loaded transition with id {0}", transition.TransitionId);

        // execute just to show how it's done
        transition.PerformTransition();
    }

    Console.Read(); // pause
}

您会注意到该方法引用了AbstractTransition类。现在让我们为它创建一个单独的TransitionModule项目。这是模块将引用的项目:

namespace TransitionModule
{
    public abstract class AbstractTransition
    {        
        public readonly int TransitionId;
        public abstract void PerformTransition();

        protected AbstractTransition(int transitionId)
        {
            TransitionId = transitionId;
        }

        // you can add functionality here as you see fit
    }
}

既然我们有一个抽象的过渡类来实现插件和一个正常运行的插件加载器,我们可以继续创建一些过渡插件。

我在我的解决方案中为此创建了一个Modules文件夹,但这并不重要。

FlipTransition项目中的第一个模块:

using System;
using TransitionModule;

namespace FlipTransition
{
    public class FlipTransition : AbstractTransition
    {
        public FlipTransition() : base(2)
        {
        }

        public override void PerformTransition()
        {
            Console.WriteLine("Performing flip transition");
        }
    }
}

SlideTransition项目中的第二个模块:

using System;
using TransitionModule;

namespace SlideTransition
{
    public class SlideTransition : AbstractTransition
    {
        public SlideTransition() : base(1)
        {
        }

        public override void PerformTransition()
        {
            Console.WriteLine("Performing slide transition");
        }
    }
}

请注意,每个项目都需要引用TransitionModule项目,但主项目不需要了解任何其他项目。

现在我们有2个转换插件和一个插件加载器。由于插件加载器将从Modules目录加载模块,因此请转到主项目的/bin/Debug目录并创建一个Modules目录。将转换插件项目的/bin/Debug目录中的所有dll复制到该目录中。所有这些都可以在以后的构建后任务中实现自动化。

继续运行程序。你应该得到这样的输出:

Loaded transition with id 2
Performing flip transition
Loaded transition with id 1
Performing slide transition

你可以做很多事情来使它更优雅,但这至少是一个简单的例子,说明如何使用基于插件的架构来提供你需要的东西。

答案 1 :(得分:0)

您可以尝试使用抽象类,为图像过渡创建基本抽象类;

public abstract class ImageTransition
{
    protected int imageId { get; set; }
    public Dictionary<int, Image> ImageDictionary { get; set; }

    protected abstract void TransitionToNextImageId();

    public Image GetNextImage()
    {
        TransitionToNextImageId();
        return ImageDictionary[imageId];
    }
}

然后创建从此基类继承的新Transition类型,并拥有自己的TransitionToNextImageId方法实现;

public class InTurnImageTransition : ImageTransition
{
    protected override void TransitionToNextImageId()
    {
        if(this.imageId < ImageDictionary.Count)
            this.imageId ++;
    }
}

public class RandomImageTransition : ImageTransition
{
    protected override void TransitionToNextImageId()
    {
        imageId = new Random().Next(0, ImageDictionary.Count);
    }
}

这允许您根据需要构建一些自定义转换。

<强> - 编辑 - 在调用GetNextImage方法之前,您当然会填充字典ImageDictionary。