IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    如何使用 C++ 11 实现 C# 属性概念设计

    Plato发表于 2016-08-08 09:31:56
    love 0

    1、概述

    本人对“C++实现C#属性概念”的研究决定并非一时冲动,而是原自于对技术的热爱。

    用过C#的C渣渣程序员/媛都非常喜欢C#里面的属性的概念(注意,C#里的属性和成员变量之间是有区别的),可是C渣渣里并没有属性这个概念,于是,很多程序员/媛都想办法使用C渣渣抽象出类似C#中的属性的概念,当然我也不例外。

    看过很多网友and同事写的C++属性实现方案,But似乎都没有找到很符合口味的实现,不是太难看懂就是太难使用。

    so,本文介绍的是博主在工作过程中封装的C++实现属性的方法,不敢说多好用,但从使用形式上看,个人觉得与我了解到的方案中,博主实现的方案有如下特点:

    1、易用且非常接近C#的使用风格。

    2、代码量少,很容易看懂。

    下面将一步一步介绍一些实现上的细节。

    2、C# 属性的概念

    本想略过对C#属性的介绍,但考虑到读者可能没使用过C#,也不知道C#的属性到底有什么用,因此还是稍微从使用的角度先解释一下C#中属性的概念(如果您对属性已经足够了解,可直接跳过本章或直接从下面第2点的“C++实现效果与C#效果对比”开始)。

    一下内容包含如下部分:

    1、简单示例代码介绍C#中的属性;

    2、C++实现效果与C#效果对比;

    1、简单示例代码介绍 C# 中的属性

    我们来看第一段C#代码:

    //示例代码1.0 http://www.cnblogs.com/feng-sc/p/5742689.html
    class TestDemo
    {
        public String Name
        {
            get;
            set;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            TestDemo test = new TestDemo();
            test.Name = "test";
            System.Console.WriteLine(test.Name);
        }
    }

    上面代码中,我们创建了一个String类型的属性Name,这个时候我们,我们的属性其实跟一个成员变量没啥区别,只是一个成员变量罢了。

    另外,还需要读者了解的是,属性里的get和set分别控制属性的读写权限:

    1、如果只有get,那么Name为只读模式;

    2、如果只有set,那么Name为只写模式;

    只读模式示例代码:

    //示例代码1.0 http://www.cnblogs.com/feng-sc/p/5742689.html
    class TestDemo
    {
        public String Name
        {
            get;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            TestDemo test = new TestDemo();
            test.Name = "test";//只读属性,赋值不允许,编译出错
            System.Console.WriteLine(test.Name);
        }
    }

    只写模式示例代码:

    //示例代码1.0 http://www.cnblogs.com/feng-sc/p/5742689.html
    class TestDemo
    {
        public String Name
        {
            set;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            TestDemo test = new TestDemo();
            test.Name = "test";
            System.Console.WriteLine(test.Name);  //只写模式,test.Name取值不允许,编译失败
        }
    }

    我们再来看一个复杂点的例子:

    //示例代码1.0 http://www.cnblogs.com/feng-sc/p/5742689.html
    class TestDemo
    {
        public String Name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
                TestWork();
            }
        }
        public void TestWork()
        {
            System.Console.WriteLine(_name);
        }
    
        private String _name;
    }
    class Program
    {
        static void Main(string[] args)
        {
            TestDemo test = new TestDemo();
            test.Name = "test";
        }
    }

    以上代码中,我们运行到第28行的时候,会进入到第10行,最终运行结果打印出“test”。

    是的,其实很简单,如果没有属性,我们完全可以用一个私有的成员函数,然后为每个成员函数添加一个get和set的方法(java下的通常做法),如下代码中的TestDemo完全能实现上面代码中属性的做法:

    //示例代码1.0 http://www.cnblogs.com/feng-sc/p/5742689.html
    class TestDemo
    {
        public String GetName()
        {
            return _name;
        }
        public void SetName(String value)
        {
            _name = value;
            TestWork();
        }
        public void TestWork()
        {
            System.Console.WriteLine(_name);
        }
    
        private String _name;
    }
    ……

    OK,那为什么要有属性呢?

    其实追求的真是一种书写方法吧,属性能让我们像访问成员变量一样访问直接赋值和取值,同时,在赋值和取值的时候,能根据需求调用相应的内部实现函数。

    2、C++实现效果与C#效果对比

        

    请读者对比下左右两边的属性Name的定义方式,C#里面的get和set方法在C++11实现中,使用了property_getter和property_setter实现,property_getter和property_setter中代码的实现方式与C#均一致。

    OK,看完属性效果比较,如果您觉得对你的胃口,那么可以继续往下看,下面会有关于proerty_rw更详细的说明。

    3、如何使用 C++ 11 实现 C# 属性的概念模式

    本章我们将一步步介绍C++11实现属性三种形式分别是:

    1、property_rw :对应C#的读写模式(get和set均有)

    2、property_r :对应C#的只读模式(没有set)

    3、property_w  :对应C#的只写模式(没有get)

    1、property_rw 介绍

    property_rw的实现代码很简单,但需要大家对C++11中的std::function和lamda表达式有所了解,如果您不是很了解或在下面介绍中觉得难懂,可以先看看我之前写的关于C++11的总结文章:【干货】C++11常用特性的使用经验总结,对您理解本章内容会有帮助。

    proerty_rw源码:

    //示例代码1.0 http://www.cnblogs.com/feng-sc/p/5742689.html
    #include<functional>
    #define property_setter(variableType) [&](variableType value)
    #define property_getter(variableType) [&]()->variableType
    template <typename ValueType>
    class property_rw
    {
    public:
        typedef std::function<void(ValueType value)> Setter;
        typedef std::function<ValueType(void)> Getter;
        explicit property_rw(Setter setter, Getter getter) : m_setter(setter), m_getter(getter) {}
        property_rw& operator=(ValueType value)
        {
            this->value = value;
            m_setter(this->value);
            return *this;
        }
        property_rw& operator=(const property_rw & instance)
        {
            this->value = instance.m_getter();
            m_setter(this->value);
            return *this;
        }
        operator ValueType()
        {
            return m_getter();
        }
    private:
        ValueType value;
        Setter m_setter;
        Getter m_getter;
    };

    上面代码我们可以看出,property_rw是一个模板类,ValueType为属性的类型名,因此大家可以想到,我们的属性其实是一个类对象。

    因此,我们来看一个最简单的使用示例:

    //示例代码1.0 http://www.cnblogs.com/feng-sc/p/5742689.html
    #includestring>
    #include"property.h"
    class TestDemo
    {
    public:
        property_rwstring> Name;
    };

    需要注意:#include需要放在#include”property.h”之前,同理,其他非基本数据类型引用头文件均需如此,这主要是由于proerty.h内部宏定义的property_setter和property_getter所导致,要保持属性的书写风格,目前暂时没有很好的解决这个问题。

    上面的代码无法编译通过,但我们先看看形式,我们定义了一个string类型的属性Name。

    从proerty_rw的构造函数看explicit property_rw(Setter setter, Getter getter),我们定义这个属性,需要给这个属性赋值两个参数,分别为Setter和Getter。Getter和Setter类型分别为std::function定义的两个可执行对象类型。在【干货】C++11常用特性的使用经验总结文章中,我们介绍了std::function定义的可执行对象可以有三种形式的赋值,分别是:

    1、同形式(返回值和参数相同)函数指针;

    2、同形式的类成员函数;

    3、同形式的lamda表达式函数;

    为了统一外面的使用形式,我们使用宏的方式定义(property_rw源码第3、4行)property_getter和proerty_setter,该宏定义其实是限制外部使用lamda表达式方式(当然,这没能从编译源头限制,其实外部还是可以使用第1、2两种方式)。我们来看下property_getter和proerty_setter的定义:

    #define property_setter(variableType) [&](variableType value)  //定义lamda表达式的头部,[&]表示对定义范围内的变量取值权限为引用形式,参数为variableType
    #define property_getter(variableType) [&]()->variableType    //property_getter的lamda表达式返回值为variableType

    明白了property_getter和proerty_setter的定义,我们来看下Name的初始化:

    //示例代码1.0 http://www.cnblogs.com/feng-sc/p/5742689.html
    class TestDemo
    {
    public:
        property_rwstring> Name = property_rwstring>(
            property_setter(std::string)
            {
    
            },
            property_getter(std::string)
            {
                return "test";
            }
        );
    };

    这个时候我们来看Name的初始化就非常清晰了,其实就是给property_rw构造函数传递了两个lamda表达式定义的函数,经过我们的宏封装,使我们的属性使用风格看起来与C#形式很像。

    我们再来看看property_rw 中的函数,控制读取属性的函数:

    //示例代码1.0 http://www.cnblogs.com/feng-sc/p/5742689.html
    property_rw& operator=(ValueType value)
    {
        this->value = value;
        m_setter(this->value);
        return *this;
    }
    property_rw& operator=(const property_rw & instance)
    {
        this->value = instance.m_getter();
        m_setter(this->value);
        return *this;
    }

    加入我们现在有两个属性string类型的属性,名字分别为Name1,Name2,那么什么时候会调用上面两个函数呢?

    TestDemo test;
    test.Name1 = "test";   //调用第一个operator=()函数
    test.Name2 = test.Name1; //调用第二个operator=()函数

    property_rw中控制写熟悉的函数:

    operator ValueType()
    {
        return m_getter();
    }

    示例:

    TestDemo test;
    test.Name = "test";
    std::string str = test.Name //调用写属性控制函数

    小结:

    property_rw的模板类实现简单吧,没有太多的逻辑代码,都是普通的类设计而已,关键需要大家有一些编码技巧。 

    2、property_r、property_w 介绍

    property_r和property_w就更加简单了,我么只需要把property_rw中的控制读或控制写的属性函数去掉,就可以变成只读或只写的属性类型。

    property_r 源码:

    //示例代码1.0 http://www.cnblogs.com/feng-sc/p/5742689.html
    template class property_r
    {
    public:
        typedef std::functionvoid(ValueType value)> Setter;
        typedef std::functionvoid)> Getter;
        explicit property_r(Getter getter) : m_getter(getter) {}
        operator ValueType()
        {
            return m_getter();
        }
    private:
        ValueType value;
        Getter m_getter;
    };

      property_w源码:

    //示例代码1.0 http://www.cnblogs.com/feng-sc/p/5742689.html
    template class property_w
    {
    public:
        typedef std::functionvoid(ValueType value)> Setter;
        explicit property_w(Setter setter) : m_setter(setter) {}
        property_w& operator=(ValueType value)
        {
            this->value = value;
            m_setter(this->value);
            return *this;
        }
        property_w& operator=(const property_w & instance)
        {
            this->value = instance.m_getter();
            m_setter(this->value);
            return *this;
        }
    private:
        ValueType value;
        Setter m_setter;
    };

    property_r和property_w就没有什么可以介绍的了,基本和property_rw类似。

    3、完整属性测试代码介绍

    下面是一个比较完整的测试例子:

    //示例代码1.0 http://www.cnblogs.com/feng-sc/p/5742689.html
    #include
    #includestring>
    #include"property.h"
    class TestDemo
    {
    public:
        property_rwstring> Name = property_rwstring>(
            property_setter(std::string)
            {
                m_rw_name = value;
            },
            property_getter(std::string)
            {
                return m_rw_name;
            }
        );
        property_rstring> ReadOnlyName = property_rstring>(
            property_getter(std::string)
            {
                return m_readonly_name;
            }
        );
        property_wstring> WriteOnlyName = property_wstring>(
            property_setter(std::string)
            {
                m_writeonly_name = value;
                TestWork();
            }
        );
        void TestWork()
        {
            std::cout "TestWork()::m_writeonly_name--" std::endl;
        }
    private:
        std::string m_rw_name;
        std::string m_readonly_name = "I m read only name";
        std::string m_writeonly_name = "";
    };
    
    int main()
    {
        TestDemo test;
        test.Name = "This is test name!";
        std::string str = test.Name;
        std::string readonly = test.ReadOnlyName;
        std::cout "Test read and write,Name:"  std::endl;
        std::cout "Test readonly, msg:" readonly  std::endl;
        test.WriteOnlyName = "This is write only property!";
    }

    运行结果:

      

    4、总结

    本文我们首先介绍了C#属性概念,以及为什么使用属性,再一步一步引出我们如何使用C++11实现C#属性的效果。整篇文章所述没有太多逻辑的问题,单纯用我们所熟知的C++11知识加上我们的一些编码技巧,最后我们用很普通的做法实现了我们想要的效果。编程有时候给人的感觉就是这样,先要有一个好的概念(如本文中的属性概念),然后想办法用我们熟知的知识需完美地把它实现,这会让你蛮有成就感!

    相关文章

    • 如何在 Linux 下检测内存泄漏
    • 编写高质量代码
    • C++ 线程池实现原理
    • C++11 中的右值引用与转移语义
    • C++11 中的 Defaulted 和 Deleted 函数
    • Linux 下 C++ 异常处理技巧
    • C++ 11 常用特性的使用经验总结
    • C++ 之 stl::string 写时拷贝导致的问题
    • Linux 平台相关代码的 C++ 解决方案
    • C++静态库与动态库

    如何使用 C++ 11 实现 C# 属性概念设计,首发于文章 - 伯乐在线。



沪ICP备19023445号-2号
友情链接