بطور ساده و مختصر، زمانی که شما یک پروژه ایجاد میکنید و از اون پنجره های Wizard تنظیمات دلخواهتون رو اعمال میکنید تا پروژه بصورت شخصی سازی شده ایجاد بشه، درواقع دارید از T4 Template ها کمک میگیرید. T4 قالب هایی هستند که باعث میشوند کدهای سی شارپ یا ویژوال بیسیک بصورت اتوماتیک تولید بشن. نکته مثبتی که T4 داره اینه که ما میتونیم در زمان کدنویسی ازش استفاده کنیم و کدهای سی شارپ رو همون لحظه تحویل بگیریم و ازش استفاده کنیم.
اگر کنجکاوید که چرا بهش میگن T4 باید بگم که خلاصه شده ی Text Template Transformation Toolkit هست بخوایم فارسیش کنیم میشه جعبه ابزار تبدیل الگوی متن? چهارتا T اول هر کلمه رو برداشتن شده T4
حالا ما چطوری میتونیم ازش استفاده کنیم و به چه دردی میخوره؟
خوب بزارید با یه مثال بهتون نشون بدم که واقعا چقدر مفید هست:
ما میتونیم از فایل هایی با پسوند resx برای چند زبانگی و کارهایی از این دست استفاده کنیم ینی محتوامون رو داخل این فایل ها ذخیره کنیم و ازشون استفاده کنیم.
حالا وقتی میخوایم داخل Designer ویژوال استودیو از این ریسورس ها استفاده کنیم ممکنه دیزاینر نتونه اسم کلیدهارو نشون بده و Intellisence رو از دست بدیم و مجبور بشیم خودمون اسم کلید رو کامل تایپ کنیم. برای حل این مشکل ما میتونیم یه کلاس ایجاد کنیم و برای هر کلید یه پراپرتی بصورت زیر ایجاد کنیم:
public static readonly string About = nameof(About);
اما ایا منطقی هست ما هر بار که ریسورس جدیدی ایجاد میکنیم یا تغییر میدیم بیایم این کلاس رو ویرایش کنیم و کلیدهارو اصلاح کنیم؟ قطعا نه!
اما چی میشه که یچیزی بیاد این کلیدهارو برامون بکشه بیرون و همین پراپرتی هارو براش ایجاد بکنه؟ قطعا خیلی ساده تر و سریعتر خواهد بود و ما هم به نتیجه دلخواه میرسیم. اینجاست که T4 به کمک میاد.
فرض میکنیم که ما یه فایل resx با اسم Resource.resx داریم. که داخلش چند تا کلید ایجاد کردیم، در کنار این فایل یه فایل به اسم LocKey.tt ایجاد میکنیم دقت کنید که پسوندش tt هست.
حالا کدهای زیر رو کپی میکنیم:
<#@ template debug="false" hostspecific="true" language="C#" #> <#@ assembly name="System.Core" #> <#@ assembly name="System.Windows.Forms" #> <#@ output extension=".cs" #> namespace WpfApp1 { public class LocKey { <#using (var reader = new System.Resources.ResXResourceReader(this.Host.ResolvePath("Resource.resx"))) { var enumerator = reader.GetEnumerator(); while (enumerator.MoveNext()) { #>public static readonly string <#= enumerator.Key #> = nameof(<#= enumerator.Key #>);<# Write("\r\n\t\t"); } Write("\r\n"); }#> } }
توی 4 خط اول ما مشخص کردیم که قراره کدها به چه زبونی تولید بشن، پسوندش چی باشه، از چه فضای نامی قراره استفاده بشه. بعدش فضای نام و اسم کلاس پروژه رو مشخص کردیم که ما اینجا اسمش رو LocKey گذاشتیم. نکته ای که باید دقت کرد اینه که هرجا بخوایم مستقیم کد سی شارپی که تو قالب مینویسیم تو خروجی تولید بشه اون رو بدون نشانه مینویسیم اما اگر بخوایم T4 برامون کد تولید کنه باید اون رو بین نشانه <# #> قرار بدیم. الان تو قالب بالا کد زیر توسط T4 اجرا میشه که میاد فایل resx رو باز میکنه و کلیدهارو میکشه بیرون و خودش تو خروجی ما دیده نمیشه:
using (var reader = new System.Resources.ResXResourceReader(this.Host.ResolvePath("Resource.resx"))) { var enumerator = reader.GetEnumerator(); while (enumerator.MoveNext()) { #>public static readonly string <#= enumerator.Key #> = nameof(<#= enumerator.Key #>);<# Write("\r\n\t\t"); } Write("\r\n"); }
و اگر دقت بکنید من کد پراپرتی رو مستقیم نوشتم بجز جاهایی که نیاز به اسم کلید هست و اون قسمت رو به کمک T4 کامل کردم
public static readonly string <#= enumerator.Key #> = nameof(<#= enumerator.Key #>);
حالا اگه فایل رو ذخیره کنیم بلافاصله همه کلیدها بصورت پراپرتی ایجاد میشن