概述

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 调用方法(注意: 这里使用这些类作为纯函数的类,故使用类似单例的用法。也可以在初始化记录类型,通过反射创建多个实例。)

最新文章

  1. QT_地图导航
  2. Go语言 获取get、post参数
  3. struts2+jsp+hiberbate 双重遍历
  4. Conversion to Dalvik format failed:Unable toexecute dex: method ID not in [0, 0xffff]: 65536
  5. C#函数的方法定义和方法调用小议
  6. hb_gui配置heartbeat做MariaDB的高可用
  7. static成员是可以被其所在class创建的实例访问!!!
  8. Java基础(3) -字符串
  9. Kotlin for循环使用
  10. 第十九节: 结合【表达式目录树】来封装EF的BaseDal层的方法
  11. Jquery Ajax Realize whether the user is registered
  12. 阿里分布式服务框架Dubbo的架构总结
  13. golang操作mysql使用总结
  14. 接之前的文章,VS2017中使用Spring.NET配置以及使用方法(framework4.6.1超详细)
  15. shiro实战系列(十四)之配置
  16. CheeseZH: Stanford University: Machine Learning Ex5:Regularized Linear Regression and Bias v.s. Variance
  17. shell :
  18. sqlite两表更新update
  19. Java学习之Dubbo+ZooKeeper分布式服务Demo
  20. JAVA ArraySet&lt;E&gt; SET形式的有序LIST

热门文章

  1. OO_Lab2总结博客
  2. Pytorch GPU加速
  3. VSCode-关于自动格式化问题
  4. Cloneable的使用
  5. mac安装mysql5.6默认密码修改
  6. EF 操作实例
  7. mysql-连接路径url参数(随时补充及改错)
  8. 使用scrollIntoView 使某元素滚动到指定位置
  9. Ubantu12.04安装及离线安装网卡驱动
  10. [Unity热更新]Addressables