Учитывая популярность этого вопроса и интерес к такой функции, я удивлен тем, что пока нет ответа, связанного с T4.
В этом примере кода я продемонстрирую очень простой пример того, как вы можете использовать мощный шаблонизатор, чтобы делать то, что компилятор в значительной степени делает за кулисами с помощью дженериков.
Вместо того, чтобы идти по кругу и жертвовать определенностью времени компиляции, вы можете просто сгенерировать нужную вам функцию для каждого типа, который вам нравится, и использовать ее соответствующим образом (во время компиляции!).
Для этого:
- Создайте новый Текстовый шаблон файл с именем GenericNumberMethodTemplate.tt .
- Удалите автоматически сгенерированный код (большая его часть останется у вас, но некоторые не нужны).
- Добавить следующий фрагмент:
<#@ template language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
<# Type[] types = new[] {
typeof(Int16), typeof(Int32), typeof(Int64),
typeof(UInt16), typeof(UInt32), typeof(UInt64)
};
#>
using System;
public static class MaxMath {
<# foreach (var type in types) {
#>
public static <#= type.Name #> Max (<#= type.Name #> val1, <#= type.Name #> val2) {
return val1 > val2 ? val1 : val2;
}
<#
} #>
}
Вот и все. Вы сделали сейчас.
Сохранение этого файла автоматически скомпилирует его в этот исходный файл:
using System;
public static class MaxMath {
public static Int16 Max (Int16 val1, Int16 val2) {
return val1 > val2 ? val1 : val2;
}
public static Int32 Max (Int32 val1, Int32 val2) {
return val1 > val2 ? val1 : val2;
}
public static Int64 Max (Int64 val1, Int64 val2) {
return val1 > val2 ? val1 : val2;
}
public static UInt16 Max (UInt16 val1, UInt16 val2) {
return val1 > val2 ? val1 : val2;
}
public static UInt32 Max (UInt32 val1, UInt32 val2) {
return val1 > val2 ? val1 : val2;
}
public static UInt64 Max (UInt64 val1, UInt64 val2) {
return val1 > val2 ? val1 : val2;
}
}
В вашем методе main
вы можете убедиться, что у вас есть уверенность во время компиляции:
namespace TTTTTest
{
class Program
{
static void Main(string[] args)
{
long val1 = 5L;
long val2 = 10L;
Console.WriteLine(MaxMath.Max(val1, val2));
Console.Read();
}
}
}
Я опередил одно замечание: нет, это не нарушение принципа СУХОЙ. Принцип СУХОГО заключается в том, чтобы не допустить дублирования кода людьми в нескольких местах, что может затруднить обслуживание приложения.
Это совсем не так: если вы хотите изменить, вы можете просто изменить шаблон (единый источник для всего вашего поколения!), И все готово.
Чтобы использовать его с собственными пользовательскими определениями, добавьте объявление пространства имен (убедитесь, что оно совпадает с тем, в котором вы будете определять собственную реализацию) к вашему сгенерированному коду и пометьте класс как partial
. Затем добавьте эти строки в файл шаблона, чтобы он был включен в возможную компиляцию:
<#@ import namespace="TheNameSpaceYouWillUse" #>
<#@ assembly name="$(TargetPath)" #>
Давайте будем честными: это очень круто.
Отказ от ответственности: на этот образец большое влияние оказало метапрограммирование в .NET Кевина Хаззарда и Джейсона Бока, Manning Publications .