概述
C++的模板相比于C#的泛型,有很多地方都更加的靈活(雖然代價是降低了編譯速度),比如C++支持變長參數(shù)模板、支持枚舉、int等類型的值作為模板參數(shù)。
C++支持枚舉、int等類型的值作為模板參數(shù),為C++的靜態(tài)多態(tài)編程提供了很好的幫助,比如根據(jù)枚舉值編譯期確定某個對象的行為策略等(下文舉例)。但是C#對這些都是不支持,但是C#天然支持反射,這種需求可以使用反射特性來實現(xiàn)。
需求示例
定義枚舉 enum EPlant {Tree, Flower},根據(jù)枚舉的值打印Tree,Flower字符串。注意,這里的應(yīng)用場景是編譯器時的多態(tài),即編碼時便確定使用的對象的類型。
C++的實現(xiàn)
上述的例子,C++的語法支持可以天然的實現(xiàn),如下:
#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> 這里使用變長參數(shù)模板,來支持沒有傳入模板參數(shù)的情況,特化類型Print函數(shù)打印"plant"
- template<> class PrintPlant<EPlant::Tree> 模板特化的類型,在main里使用了new PrintPlant<EPlant::Tree>();語句創(chuàng)建該類型的對象。該對象打印"Tree"。
C# 實現(xiàn)
C#的模板不支持枚舉的值作為模板參數(shù),使用反射進行模擬。文章來源:http://www.zghlxwxcb.cn/news/detail-416457.html
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#可以保存類型信息到運行期,通過運行期分析類型信息創(chuàng)建對象實現(xiàn)靜態(tài)多態(tài)。文章來源地址http://www.zghlxwxcb.cn/news/detail-416457.html
- TEX類分析傳入的所有類型,篩選父類和ABTEX特性,使用父類型,ABTEX的key的類型和值來索引該類型。(這里索引是實例對象,有需求的話可以保存類型Type,使用類型通過反射創(chuàng)建對象)
- ABTEX標(biāo)記需要反射分析的類型,并且標(biāo)記key。
- Main入口獲取當(dāng)前程序集下所有的類型信息,初始化TEX
- 通過TEX.T<抽象類>(key).Func 調(diào)用方法(注意: 這里使用這些類作為純函數(shù)的類,故使用類似單例的用法。也可以在初始化記錄類型,通過反射創(chuàng)建多個實例。)
到了這里,關(guān)于C#模擬C++模板特化對類型的值的支持的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!