文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

聊聊C#中的Mixin的具体用法

2024-04-02 19:55

关注

写在前面

Mixin本意是指冰淇淋表面加的那些草莓酱,葡萄干等点缀物,它们负责给冰淇淋添加风味。在OOP里面也有Mixin这个概念,和它的本意相似,OOP里面的Mixin意在为类提供一些额外功能——在不破坏类本身或者它的继承链的基础上,在某些情况下可能会起到妙用。今天跟着小编一起来看看吧。

从一个简单例子说起

试想我们在写一个游戏引擎,创建如下类:

    class ScriptManager
    {
        public void AddScript(){}

        public void RemoveScript(){}
    }

    class EntityManager
    {
        public void AddEntity() {}

        public void RemoveEntity() {}
    }

    class AnimationManager
    {
        public void AddAnimationToWorld() {}

        public void RemoveAnimationFromWorld() {}
    }

代码非常简单,三个manager类分别控制脚本、实体和动画。但是我们突然发现,这三个类应该都是单例才合适。按照我们之前在C#中的Singleton中介绍的方法,我们这么改写一下这三个类。

在类中实现单例

最简单的,我们可以这么改

    class ScriptManager
    {
        private static ScriptManager _instance = null;
        public static ScriptManager Instance
        {
            get
            {
                if(_instance == null)
                {
                    lock(typeof(ScriptManager))
                    {
                        if(_instance == null)
                        {
                            _instance = new ScriptManager();
                        }
                    }
                }
                return _instance;
            }
        }
        public void AddScript(){}

        public void RemoveScript(){}
        private ScriptManager() {} //车门焊死,不让外部调用
    }

	class EntityManager
	{
		//类似的修改方法
	}
	
	class AnimationManager
	{
		//类似的修改方法
	}

    static void Main(string[] args)
    {
        var instance1 = ScriptManager.Instance;
        var instance2 = ScriptManager.Instance;
        var result = instance1 == instance2; //true
    }

看起来没有什么问题,确实也满足了可用的要求,但是仅仅可用是不够的,我们想要更好的解决方案,而且这种修改方法虽然简单,但如果我们想要修改的类不止这三个,或者,我们想要添加的不仅仅是单例方法,我们需要写的代码会成倍增加,所以我们想要更好的解决方案。

在父类中实现单例

很容易就能想到,既然这块代码逻辑都是一样的,我们为什么不把它提炼到父类?像这样

    class SingletonHolder<T>
        where T : class
    {
        private static T _instance = null;
        public static T Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (typeof(T))
                    {
                        if (_instance == null)
                        {
                            _instance = (T)Activator.CreateInstance(typeof(T), true); //调用非公有构造器
                        }
                    }
                }
                return _instance;
            }
        }
    }
    
    class ScriptManager : SingletonHolder<ScriptManager>
    {
		//省略
    }

    class EntityManager : SingletonHolder<EntityManager>
    {
		//省略
    }

    class AnimationManager : SingletonHolder<AnimationManager>
    {
		//省略
    }

    static void Main(string[] args)
    {
        var ScriptManager1 = ScriptManager.Instance;
        var ScriptManager2 = ScriptManager.Instance;
        var result = ScriptManager1 == ScriptManager2; //true

        var EntityManager1 = EntityManager.Instance;
        var EntityManager2 = EntityManager.Instance;
        result = EntityManager1 == EntityManager2; //true

        var AnimationManager1 = AnimationManager.Instance;
        var AnimationManager2 = AnimationManager.Instance;
        result = AnimationManager1 == AnimationManager2; //true
    }

确实可以,这样就算有再多的类需要实现单例,只要让它们继承SingletonHolder就可以了,这样的代码方便扩展也方便维护,毕竟功能逻辑都在父类里面。

不过仔细想想,这样的代码还是有点问题,类继承意味着子类应该是父类的特化,代表着一种is-a的关系,但是我们这几个Manager类和SingletonHolder并不是这种关系,它们和SingletonHolder更多像是一种实现契约的关系;如果一定要说is-a,它们应该是引擎模块(ModuleManager)的一种特化。所以让它们继承自SingletonHolder其实不是最好的方法,虽然语法正确、行为正确但是并不是语义正确,作为程序员,我们应该追求尽善尽美。而且未来真有可能会抽象出一个父类ModuleManager,到时候就发现唯一的类继承名额已经给SingletonHolder给占用了,所以我们需要寻找一种既能注入逻辑代码,又不涉及类继承的方法。

轮到Mixin出场

定义

In object-oriented programming languages, a mixin (or mix-in) is a class that contains methods for use by other classes without having to be the parent class of those other classes. How those other classes gain access to the mixin's methods depends on the language. Mixins are sometimes described as being "included" rather than "inherited".
Mixins encourage code reuse and can be used to avoid the inheritance ambiguity that multiple inheritance can cause (the "diamond problem"), or to work around lack of support for multiple inheritance in a language. A mixin can also be viewed as an interface with implemented methods. This pattern is an example of enforcing the dependency inversion principle.

这是在Wiki上面Mixin的定义,允许程序员以在类继承之外的方式为类添加一些方法,即,既能为类提供方法实现,又可以避免成为类的父类,避免了类继承和多重继承所带来的问题,这种概念正是我们需要的。

Mixin在C#中

在C#中,它们通常以拥有实现的接口出现(default implementation interface from C#8.0),而在C#8.0之前,我们通常以辅助类的方式来实现Mixin,我们下面以这两种方式改写之前的类。

在8.0之前

我们定义出一个接口,然后在外部基于这个接口实现单例逻辑(不用扩展方法是因为扩展方法不支持static method,如果想要注入的是非static method可以使用基于接口的扩展方法)

    class SingletonHolder<T>
        where T : class, ISingleton
    {
        private static T _instance = null;
        public static T Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (typeof(T))
                    {
                        if (_instance == null)
                        {
                            _instance = (T)Activator.CreateInstance(typeof(T), true);
                        }
                    }
                }
                return _instance;
            }
        }
    }

    interface ISingleton
    {
        //没有任何方法因为只是一个标记
    }

    class ScriptManager : ISingleton
    {
        private ScriptManager() {}
        public void AddScript(){}

        public void RemoveScript(){}
    }

    class EntityManager : ISingleton
    {
        private EntityManager() {}
        public void AddEntity() {}

        public void RemoveEntity() {}
    }

    class AnimationManager : ISingleton
    {
        private AnimationManager() {}
        public void AddAnimationToWorld() {}

        public void RemoveAnimationFromWorld() {}
    }

    static void Main(string[] args)
    {
        var ScriptManager1 = SingletonHolder<ScriptManager>.Instance;
        var ScriptManager2 = SingletonHolder<ScriptManager>.Instance;
        var result = ScriptManager1 == ScriptManager2; //true

        var EntityManager1 = SingletonHolder<EntityManager>.Instance;
        var EntityManager2 = SingletonHolder<EntityManager>.Instance;
        result = EntityManager1 == EntityManager2; //true

        var AnimationManager1 = SingletonHolder<AnimationManager>.Instance;
        var AnimationManager2 = SingletonHolder<AnimationManager>.Instance;
        result = AnimationManager1 == AnimationManager2; //true
    }

这就是Mixin的用处,看起来这种实现方式的好处有:

从C#8.0开始

从C#8.0开始,接口可以有方法的默认实现(包括static method),我们可以更加简单的实现Mixin解决之前的问题

    interface SingletonHolder<T>
        where T:class
    {
        private static T _instance = null;
        static T Instance
        {
            get
            {
                if(_instance == null)
                {
                    lock(typeof(T))
                    {
                        if(_instance == null)
                        {
                            _instance = (T)Activator.CreateInstance(typeof(T), true);
                        }
                    }
                }
                return _instance;
            }
        }
    }
    class ScriptManager : SingletonHolder<ScriptManager>{}
    class EntityManager : SingletonHolder<EntityManager>{}
    class AnimationManager : SingletonHolder<AnimationManager>{}

这就是Mixin以及它在C#中的简单使用方法,希望通过这篇介绍能让大家对这种用法有所了解,在想要给类添加代码逻辑但是又不想改变类内部或者影响类的继承体系的时候,使用Mixin这种基于接口的代码逻辑注入也许能有奇效哦。

到此这篇关于聊聊C#中的Mixin的具体用法的文章就介绍到这了,更多相关C# Mixin用法内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯