概述

C++的模板相比于C#,有很多地方都更加的灵活(虽然代价是降低了编译速度),比如C++支持变长参数模板、支持枚举、int等类型的值作为模板参数。

C++支持枚举、int等类型的值作为模板参数,为C++的静态多态编程提供了很好的帮助,比如根据枚举值编译期确定某个对象的行为策略等(下文举例)。但是C#对这些都是不支持,但是C#天然支持反射,这种需求可以使用反射特性来实现。

需求示例

定义枚举 enum EPlant {Tree, Flower},根据枚举的值打印Tree,Flower字符串。注意,这里的应用场景是编译器时的多态,即编码时便确定使用的对象的类型。

C++的实现

上述的例子,C++的语法支持可以天然的实现,如下:

#include <iostream>

enum class EPlant
{
Tree = 0,
Flower,
}; template<EPlant ...Args>
class PrintPlant
{ }; template<>
class PrintPlant<>
{
public:
void Print()
{
std::cout << "Plant" << std::endl;;
}
}; template<>
class PrintPlant<EPlant::Tree>
{
public:
void Print()
{
std::cout << "Tree" << std::endl;;
}
}; template<>
class PrintPlant<EPlant::Flower>
{
public:
void Print()
{
std::cout << "Flower" << std::endl;
}
}; int main()
{
auto plant = new PrintPlant<>();
plant->Print();
auto flower = new PrintPlant<EPlant::Flower>();
flower->Print();
auto tree = new PrintPlant<EPlant::Tree>();
tree->Print();
}

输出:

  • template<EPlant ...Args> 这里使用变长参数模板,来支持没有传入模板参数的情况,特化类型Print函数打印"plant"
  • template<> class PrintPlant<EPlant::Tree> 模板特化的类型,在main里使用了new PrintPlant<EPlant::Tree>();语句创建该类型的对象。该对象打印"Tree"。

C# 实现

C#的模板不支持枚举的值作为模板参数,使用反射进行模拟。

using System;
using System.Reflection;
using System.Collections.Generic; [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class ABTEX : Attribute
{
public object key; public ABTEX(object k)
{
key = k;
}
} public class TEX
{
static Dictionary<Type, Dictionary<Type, Dictionary<string, object>>> dict;
public static void Init(Type[] types)
{
dict = new();
foreach (var t in types)
{
var ABTEX = t.GetCustomAttribute<ABTEX>();
var bt = t.BaseType;
if (ABTEX != null && bt != null)
{
AddInst(t, bt, ABTEX.key);
}
}
} static string FmtKey(object key)
{
return $"{key}";
} static void AddInst(Type ty, Type bt, object key)
{
if (!dict.ContainsKey(bt))
{
dict[bt] = new();
} var kt = key.GetType();
string k = FmtKey(key); if (!dict[bt].ContainsKey(kt))
{
dict[bt][kt] = new();
} dict[bt][kt][k] = Activator.CreateInstance(ty);
} static public R T<R>(object key)
{
if (dict.TryGetValue(typeof(R), out Dictionary<Type, Dictionary<string, object>> dbt))
{
var kt = key.GetType();
string k = FmtKey(key);
if (dbt.TryGetValue(kt, out Dictionary<string, object> kbt))
{
if (kbt.TryGetValue(k, out object ins))
{
return (R)ins;
}
}
} return default(R);
}
} public enum EPlant : int
{
None = 0,
Tree,
Flower,
} public class APrintPlant
{
public virtual void Print()
{
Console.WriteLine("Plant");
}
} [ABTEX(EPlant.Tree)]
public class PrintTree : APrintPlant
{
public override void Print()
{
Console.WriteLine("Tree");
}
} [ABTEX(EPlant.Flower)]
public class PrintFlower : APrintPlant
{
public override void Print()
{
Console.WriteLine("Flower");
}
} class Program
{
static void Main(string[] args)
{
var all = Assembly.GetExecutingAssembly().GetTypes();
TEX.Init(all);
TEX.T<APrintPlant>(EPlant.Tree).Print();
TEX.T<APrintPlant>(EPlant.Flower).Print();
}
}

输出:



C#可以保存类型信息到运行期,通过运行期分析类型信息创建对象实现静态多态。

  • TEX类分析传入的所有类型,筛选父类和ABTEX特性,使用父类型,ABTEX的key的类型和值来索引该类型。(这里索引是实例对象,有需求的话可以保存类型Type,使用类型通过反射创建对象)
  • ABTEX标记需要反射分析的类型,并且标记key。
  • Main入口获取当前程序集下所有的类型信息,初始化TEX
  • 通过TEX.T<抽象类>(key).Func 调用方法(注意: 这里使用这些类作为纯函数的类,故使用类似单例的用法。也可以在初始化记录类型,通过反射创建多个实例。)

C#模拟C++模板特化对类型的值的支持的更多相关文章

  1. C++-函数模板特化如何避免重复定义

     我正在用一个基于模板的库源代码,该库包含一些针对特定类型的模板函数特化.类模板,函数模板和模板函数特化都在头文件中.我在我的.cpp文件中 #include 头文件并编译链接工程.但是为了在整个工程 ...

  2. C++的模板特化 和 STL中iterator_traits模板的偏特化

    C++中有类模板和函数模板,它们的定义如下所示: 类模板: template<class T1,class T2> class C { //... }; 函数模板: template< ...

  3. C++ Primer 学习笔记_84_模板与泛型编程 --模板特化

    模板与泛型编程 --模板特化 引言: 我们并不总是能够写出对全部可能被实例化的类型都最合适的模板.某些情况下,通用模板定义对于某个类型可能是全然错误的,通用模板定义或许不能编译或者做错误的事情;另外一 ...

  4. C++ Primer 学习笔记_85_模板与泛型编程 --模板特化[续]

    模板与泛型编程 --模板特化[续] 三.特化成员而不特化类 除了特化整个模板之外,还能够仅仅特化push和pop成员.我们将特化push成员以复制字符数组,而且特化pop成员以释放该副本使用的内存: ...

  5. C++程序设计方法4:模板特化

    模板参数的具体化/特殊化 有时,有些类型不适用,则需要对模板进行特殊化处理,这称为“模板特化” 对函数模板,如果有多个模板参数,则特化时必须提供所有参数的特例类型,不能部分特化: 如: char *s ...

  6. C++ template —— 模板特化(五)

    本篇讲解模板特化-------------------------------------------------------------------------------------------- ...

  7. C++ 模板特化以及Typelist的相关理解

    近日,在学习的过程中第一次接触到了Typelist的相关内容,比如Loki库有一本Modern C++ design的一本书,大概JD搜了一波没有译本,英文版600多R,瞬间从价值上看到了这本书的价值 ...

  8. 转:C++模板特化的概念

    http://blog.csdn.net/yesterday_record/article/details/7304025 很久没有看C++,在看STL源码剖析时,看到一个function templ ...

  9. oop &&GP 模板 ---> 特化和偏特化

    OOP面向对象编程 GP泛型编程(generic programming) 两者的主要区别就是OOP将数据和对数据的操作放在一起, GP就是将数据和操作独立开来 GP:   数据就是container ...

  10. 对C++ templates类模板的几点补充(Traits类模板特化)

    前一篇文章<浅谈C++ templates 函数模板.类模板以及非类型模板参数>简单的介绍了什么是函数模板(这个最简单),类模板以及非类型模板参数.本文对类模板再做几点补充. 文章目录1. ...

随机推荐

  1. CountDownLatch/CyclicBarrierDemo/Samaphore

    CountDownLatch CountDownLatch:让一些线程阻塞直到另外一些完成后才被唤醒 CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,调用线程会被阻塞 ...

  2. porps传参

    porps传参(最常用的 布尔传值)(基于前面的步骤进行修改) ①index.js //定义动态路由 props:trueconst routes =[ {path:"/user/:id/: ...

  3. SQLyog中创建的数据库在idea找不到

    在里面把需要的数据库

  4. gongwen

    gongwen 学号 姓名 工作占比 20201307 梁辰鱼 20% 20201323 谭顺心 17% 20201222 龚 杰 16.5% 20201325 夏俊睿 16% 20201317 鲁永 ...

  5. 嵌入式数据库 sqllite & h2  utils

    使用场景: 简单脚本,但是有需要数据记录. (使用前升级下版本) 我的使用: 老机器,老项目,jkd6,  需要记录 SqlLiteUtils package com.sea.edi.listener ...

  6. Python库之os库和logging库的基本使用说明

    使用os库操作目录及文件 使用os.sep() 方法获取系统分隔符 print(os.sep) 使用os.name()方法获取操作系统的平台类型 print(os.name) 使用os.getcwd( ...

  7. Javaweb基础知识复习------AJAX

    AJAX相关知识复习 简而言之,就是可以用AJAX+HTML代替JSP页面,也可以进行异步交互,更新部分界面 Ajax案例 后端代码就是一个servlet文件,前端页面的代码也不是很常用,可以在下面这 ...

  8. 【深入浅出 Yarn 架构与实现】5-1 Yarn 资源调度器基本框架

    资源调度器是 YARN 中最核心的组件之一,它是 ResourceManager 中的一个插拔式服务组件,负责整个集群资源的管理和分配. Yarn 默认提供了三种可用资源调度器,分别是FIFO (Fi ...

  9. 跨域解决方案CORS

    这里说的 js 跨域是指通过 js 在不同的域之间进行数据传输或通信,例如通过 ajax 向一个不同的域请求数据,或者通过 js 获取页面中不同域中(iframe)的数据.只要协议.域名.端口有任何一 ...

  10. XAML 设计器已意外退出。(退出代码: e0434352)

    一.前言 开门见山,这个问题我遇到过两次,第一次因为项目刚开始不长时间,我查了很长时间都没解决,然后就直接重写了,几乎一样的写法,但问题没复现了,但程序员思维告诉我,一定还是有比较关键的地方出现了问题 ...