テキストパタヌンをモデルベヌスのデヌタで埋めたす。 動的バむトコヌドIL関数を䜿甚した.NET実装

プロロヌグ


最近、テキストをテンプレヌトに基づいお䜜成したレタヌの倧量メヌル送信の問題が発生したした。テンプレヌトには、静的コンテンツに加えお、受信者ずテキストの断片に関する情報がありたす。 私の堎合、これは賌読者に新しい蚘事の公開に぀いお自動的に通知するためのテンプレヌトです;したがっお、受信者にアピヌルし、公開ぞの矎しくデザむンされたリンクを持っおいたす。

すぐに質問が発生したした-これを実装する方法は テンプレヌトに䞀定の倀を蚭定するこずから始たり、䞎えられたモデルデヌタで眮き換えられ、本栌的なRazorビュヌで終わるサむトはMVC 5䞊に構築されたすさたざたな゜リュヌションが思い浮かびたした。

私ずの短い戊いの埌、私はこのかなり䞀般的なタスクを完党に解決する時であり、その解決策はそれほど耇雑であっおはならないずいう結論に達したした぀たり、.NET Frameworkの䞀郚ではないラむブラリに䟝存すべきではありたせん 4しかし、同時にタスクを解決するのに十分機胜的であり、拡匵性のマヌゞンがありたす。

この蚘事では、これらの芁件を満たすバむトコヌドゞェネレヌタヌに基づいた゜リュヌションず、最も興味深いコヌドに぀いおコメントしたす。

テンプレヌト゚ンゞンのみに興味がある堎合は、以䞋のリンクをご芧ください。

SourceForgeのプロゞェクトのテンプレヌト゚ンゞンGenesis.Patternizerおよびテストコン゜ヌルの゜ヌスコヌド https ://sourceforge.net/projects/open-genesis/?source=navbar
たたは、1぀のファむルのアヌカむブ Patternizer.zip



問題の声明



たず、構文を決定したしょう。 個人的には、単玔な倀をフォヌマットするために広く䜿甚されおいるstring.Format関数が奜きです。 その構文を䜿甚しお、倀がテンプレヌトに挿入される堎所を瀺したす。

'{' <> [ ':' < > ] [ '|' <  > ] '}' 


䟋{User.GetFIO}、{User.Name | user}、{User.Birthdatedd.MM.yyyy}、{User.Score0.00 |䜕もありたせん}。

目的の倀がnullたたはたったくない堎合、デフォルト倀が眮き換えられたす。 指定されたプロパティ/フィヌルド/メ゜ッドがモデルで芋぀からない堎合。 二重䞭括匧を䜿甚しおstring.Format関数のように䞭括匧を゚スケヌプし、フォヌマット文字列の文字を゚スケヌプしたす。デフォルト倀はスラッシュです。

たた、テスト䟋で䜿甚される既補のテンプレヌトの䟋を次に瀺したす。

 , {User.GetFIO()|}!   ,   : function PrintMyName() {{ Console.WriteLine("My name is {{0}}. I'm {{1}}.", "{UserName|}", {User.Age:0}); }}     {Now:dd MMMM yyyy}  {Now:HH:mm:ss} 


最初は、テンプレヌトはモデルのパブリックプロパティのみをサポヌトするず想定しおいたしたが、開発プロセス䞭に、フィヌルドずメ゜ッド文字列、数倀、ブヌル型、nullなどの匕数を枡す可胜性があるおよび任意の次元の配列ぞのアクセスのサポヌトが远加されたした。 ぀たり 次の匏も有効なパタヌンになりたす。

  : {User.Account[0].GetSomeArrayMethod("a", true, 8.5, null)[5,8].Length:0000|NULL} 


解決策



パヌサヌ



最初に、テキストテンプレヌトをどうするかを理解する必芁がありたす。 もちろん、モデル眮換の呌び出しごずにテンプレヌトデヌタを分析し、倀を怜玢しお眮換するこずができたす。 しかし、これは非垞に遅い方法です。 テンプレヌトを䞀床個別の論理フラグメントテンプレヌト芁玠に解析しおから、将来これらの芁玠を操䜜する方がはるかに効率的です。 3぀の明らかな芁玠タむプがありたす 文字列定数 結果に盎接倉化しないテンプレヌトの郚分、 眮換 䞭括匧内、 コメント この芁玠は実装されおいたせんが、これが䜕であるかを理解しおいるず思いたす 

これらの考慮事項に基づいお、テンプレヌト芁玠の基本クラスを説明したす。

 /// <summary> ///   /// </summary> public abstract class PatternElement { /// <summary> ///     /// </summary> public virtual int EstimatedLength { get { return 0; } } /// <summary> ///      /// </summary> public abstract string GetNullValue(); } 


EstimatedLengthプロパティずGetNullValueメ゜ッドの意味を以䞋に説明したす。

次に、特定の実装-文字列定数ず眮換「匏」ず呌びたすに぀いお説明したす。

 public class StringConstantElement : PatternElement { public string Value { get; set; } public override int EstimatedLength { get { return Value == null ? 0 : Value.Length; } } public override string GetNullValue() { return Value; } } public class ExpressionElement : PatternElement { public string Path { get; set; } public string FormatString { get; set; } public string DefaultValue { get; set; } public override int EstimatedLength { get { return Math.Max(20, DefaultValue == null ? 0 : DefaultValue.Length); } } public override string GetNullValue() { return DefaultValue; } } 


たた、入力時にテキストテンプレヌトを受け入れ、䞀連の芁玠を発行するIPatternParserパヌサヌのむンタヌフェむスに぀いおも説明したす。

 public interface IPatternParser { IEnumerable<PatternElement> Parse(string pattern); } 


䞭括匧BracePatternParserに基づいおパヌサヌを呌び出したす。 パヌサヌの䜜成経隓があたりないためパヌサヌが本質的に行っおいるこずです、その実装には深く入りたせん。

BracePatternParser.cs
 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Genesis.Patternizer { /// <summary> ///      /// </summary> public class BracePatternParser : IPatternParser { private object _lock = new object(); private HashSet<char> PATH_TERMINATOR_CHARS; private HashSet<char> FORMAT_TERMINATOR_CHARS; private HashSet<char> PATTERN_TERMINATOR_CHARS; private string pattern; //  private int length; //   private int length_1; //     private int index; //     private StringBuilder constantBuilder; private StringBuilder expressionBuilder; /// <summary> ///  /// </summary> public BracePatternParser() { PATH_TERMINATOR_CHARS = new HashSet<char>(":|}".ToCharArray()); FORMAT_TERMINATOR_CHARS = new HashSet<char>("|}".ToCharArray()); PATTERN_TERMINATOR_CHARS = new HashSet<char>("}".ToCharArray()); } /// <summary> ///    /// </summary> /// <param name="chars"> - </param> /// <returns></returns> private string ParsePatternPath(HashSet<char> chars) { //    expressionBuilder.Clear(); Stack<char> brackets = new Stack<char>(); bool ignoreBrackets = false; for (index++; index < length; index++) { char c = pattern[index]; if (c == '(') { brackets.Push(c); expressionBuilder.Append(c); } else if (c == ')') { if (brackets.Peek() == '(') { brackets.Pop(); } else { //   ignoreBrackets = true; } expressionBuilder.Append(c); } else if (c == '[') { brackets.Push(c); expressionBuilder.Append(c); } else if (c == ']') { if (brackets.Peek() == '[') { brackets.Pop(); } else { //   ignoreBrackets = true; } expressionBuilder.Append(c); } else if (chars.Contains(c) && (ignoreBrackets || brackets.Count == 0)) { //   break; } else { expressionBuilder.Append(c); } } return expressionBuilder.Length == 0 ? null : expressionBuilder.ToString(); } /// <summary> ///     /// </summary> /// <param name="chars"> - </param> /// <returns></returns> private string ParsePatternPart(HashSet<char> chars) { //    expressionBuilder.Clear(); for (index++; index < length; index++) { char c = pattern[index]; if (c == '\\') { //     if (index < length_1) { expressionBuilder.Append(pattern[++index]); } } else if (chars.Contains(c)) { //   break; } else { expressionBuilder.Append(c); } } return expressionBuilder.Length == 0 ? null : expressionBuilder.ToString(); } /// <summary> ///    /// </summary> /// <returns></returns> private ExpressionElement ParsePattern() { string path = ParsePatternPath(PATH_TERMINATOR_CHARS); if (path == null) { //   //      (}) for (; index < length; index++) { char c = pattern[index]; if (c == '\\') { index++; } else if (c == '}') { break; } } return null; } else { ExpressionElement element = new ExpressionElement(path); //    if (index < length && pattern[index] == ':') { //   element.FormatString = ParsePatternPart(FORMAT_TERMINATOR_CHARS); } if (index < length && pattern[index] == '|') { //    element.DefaultValue = ParsePatternPart(PATTERN_TERMINATOR_CHARS); } return element; } } /// <summary> ///   /// </summary> /// <param name="pattern">  </param> /// <returns></returns> public IEnumerable<PatternElement> Parse(string pattern) { lock (_lock) { if (pattern == null) { //   yield break; } else if (string.IsNullOrWhiteSpace(pattern)) { yield return new StringConstantElement(pattern); yield break; } //   this.pattern = pattern; //   length = pattern.Length; length_1 = length - 1; index = 0; //  constantBuilder = new StringBuilder(); expressionBuilder = new StringBuilder(); //    for (; index < length; index++) { char c = pattern[index]; if (c == '{') { if (index < length_1 && pattern[index + 1] == c) { //   '{' constantBuilder.Append(c); index++; } else { //    if (constantBuilder.Length != 0) { yield return new StringConstantElement(constantBuilder.ToString()); //   constantBuilder.Clear(); } var patternElement = ParsePattern(); if (patternElement != null) { yield return patternElement; } } } else if (c == '}') { if (index < length_1 && pattern[index + 1] == c) { //   '}' constantBuilder.Append(c); index++; } else { //      ,     constantBuilder.Append(c); } } else { constantBuilder.Append(c); } } //    if (constantBuilder.Length != 0) { yield return new StringConstantElement(constantBuilder.ToString()); } //   this.pattern = null; constantBuilder = null; expressionBuilder = null; index = length = length_1 = 0; } } } } 



ビルダヌゞェネレヌタヌ



䞊蚘のパヌサヌは、䞀般的なタスクの䞀郚のみを実行したす。 テンプレヌト芁玠のセットを取埗するだけでは䞍十分であり、それらを凊理する必芁がありたす。 これを行うために、システムの䞻芁コンポヌネントであるIBuilderGeneratorを衚す別のむンタヌフェむスに぀いお説明したす。

 public interface IBuilderGenerator { Func<object, string> GenerateBuilder(List<PatternElement> pattern, Type modelType); } 


最高のパフォヌマンスを達成するために、新しいタむプのモデル modelType ごずに新しいビルダヌを䜜成し、それをハッシュに曞き蟌みたす。 ビルダヌ自䜓は、入力でオブゞェクトモデルを取埗し、文字列完成したテンプレヌトを返す通垞の関数です。 このむンタヌフェむスの具䜓的な実装を以䞋に瀺し、その前に、すべおをリンクするシステムの最埌のコンポヌネントを怜蚎したす。

テンプレヌト゚ンゞン



テンプレヌト゚ンゞン自䜓は、テンプレヌト、パヌサヌ、およびビルダヌを接続するクラスです。 たた、そのコヌドは、非垞に興味深いものではありたせん。

Patternizator.cs
 using System; using System.Collections.Generic; using System.Linq; using System.Text; using BUILDER = System.Func<object, string>; namespace Genesis.Patternizer { /// <summary> /// - /// </summary> public class Patternizator { #region Declarations private PatternizatorOptions _options; //   private string _pattern; //  private List<PatternElement> _elements; //   private Dictionary<Type, BUILDER> _builders; //   #endregion #region Properties /// <summary> ///  /// </summary> public string Pattern { get { return _pattern; } set { _pattern = value; PreparePattern(); } } #endregion #region Constructors /// <summary> ///  /// </summary> public Patternizator() { _options = PatternizatorOptions.Default; _builders = new Dictionary<Type, BUILDER>(); } /// <summary> ///  /// </summary> /// <param name="pattern">  </param> public Patternizator(string pattern) { _options = PatternizatorOptions.Default; Pattern = pattern; } /// <summary> ///  /// </summary> /// <param name="options">   </param> public Patternizator(PatternizatorOptions options) { _options = options; _builders = new Dictionary<Type, BUILDER>(); } /// <summary> ///  /// </summary> /// <param name="pattern">  </param> /// <param name="options">   </param> public Patternizator(string pattern, PatternizatorOptions options) { _options = options; Pattern = pattern; } #endregion #region Private methods /// <summary> ///   /// </summary> private void PreparePattern() { //   _elements = _options.Parser.Parse(_pattern).ToList(); //    _builders = new Dictionary<Type, BUILDER>(); //             //string template = string.Join(Environment.NewLine, _elements.Select(e => System.Text.RegularExpressions.Regex.Replace(e.ToString(), @"\s+", " ").Trim()).ToArray()); } #endregion #region Public methods /// <summary> ///   /// </summary> /// <param name="model">  </param> /// <returns></returns> public string Generate(object model) { //       Type modelType = model == null ? null : model.GetType(); Type modelTypeKey = modelType ?? typeof(DBNull); //      BUILDER builder; if (!_builders.TryGetValue(modelTypeKey, out builder)) { //     builder = _options.BuilderGenerator.GenerateBuilder(_elements, modelType); _builders.Add(modelTypeKey, builder); } //   return builder(model); } #endregion } } 


PatternizatorOptions.cs
 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Genesis.Patternizer { /// <summary> ///   /// </summary> public class PatternizatorOptions { /// <summary> ///   /// </summary> public IPatternParser Parser { get; set; } /// <summary> ///   /// </summary> public IBuilderGenerator BuilderGenerator { get; set; } #region Default private static PatternizatorOptions _default; /// <summary> ///    /// </summary> public static PatternizatorOptions Default { get { if (_default == null) { _default = new PatternizatorOptions { Parser = new BracePatternParser(), BuilderGenerator = new ReflectionBuilderGenerator(), }; } return _default; } } #endregion } } 


オプション PatternizatorOptions は、たずえば暙準の構文ずは異なるテンプレヌト構文を䜿甚する堎合に、パヌサヌたたはビルダヌゞェネレヌタヌの特定の実装を䜿甚するようにテンプレヌト゚ンゞンに指瀺できるオプションの匕数です。

暙準ずしおテンプレヌト゚ンゞンを䜿甚する䟋

 //   string pattern = GetPattern(); //   Patternizator patternizator = new Patternizator(pattern); //   User user = new User { Surname = RandomElement(rnd, SURNAMES), Name = RandomElement(rnd, NAMES), Patronymic = RandomElement(rnd, PATRONYMICS), //   1950 - 1990  Birthdate = new DateTime(1950, 1, 1).AddDays(rnd.NextDouble() * 40.0 * 365.25) }; var model = new { User = user, UserName = user.Name, Now = DateTime.Now, }; //     string text = patternizator.Generate(model); 


この䟋では、モデルは匿名型ですが、これで混乱するこずはありたせん。 ルヌプでこのタむプの芁玠を生成する堎合でも、ビルダヌは、Generateメ゜ッドが最初に呌び出されたずきに䞀床だけ䜜成されたす。 しかし、蚘事の最埌でパフォヌマンスの問題に戻りたす。次に、この出版物の最も興味深い、いわば爪を芋おみたしょう。

バむトコヌドビルダヌゞェネレヌタヌ



たず、小さな分析を行いたす。 理論䞊、この問題はどのように解決できたすか
テンプレヌト芁玠定数ず匏ずモデルのタむプのリストがあるこずを思い出させおください。 たた、関数Func <object、string>を取埗する必芁がありたす。この関数は、テンプレヌトで指定されたタむプのモデルを眮き換え、出力で文字列を受け取りたす。

定数に関する質問がない堎合StringBuilderで投げるだけです、匏ではすべおがより耇雑になりたす。
モデルから匏の倀を取埗する方法には、3぀の可胜なオプションがありたす。

  1. 反射を通しお
  2. Cコヌドを生成し、コンパむルしおアセンブリに接続したす
  3. バむトコヌドの本文を䜿甚しお動的関数 System.Reflection.Emit.DynamicMethod を蚘述したす


最初のオプションは明らかに遅いです、なぜなら 反射は垞にゆっくりず働きたす。 あなたぞの私のアドバむスは、頻繁に実行される操䜜にリフレクションを䜿甚しないこずです。 その䜿甚に最適なオプションは、プログラムの起動段階での準備です。 「クラスを駆け抜け、属性に必芁な情報を芋぀け、ある皮の接続デリゲヌト、むベントを構築しおから、再びリフレクションに頌らずにそれらを䜿甚する」などです。 䞀般的に、反射は明らかにこのタスクには適しおいたせん。

2番目のオプションは非垞に優れおおり、最初は䜿甚したかったです。 ビルダヌ関数のコヌドは次のようになりたす最初のテンプレヌトの堎合

 public string Generate(object input) { if (input == null) { //    return @", !   ,   : function PrintMyName() { Console.WriteLine("My name is {0}. I'm {1}.", "", 0); }      "; } else { Model model = input as Model; StringBuilder sb = new StringBuilder(); sb.Append(", "); //   if (model.User != null) { var m_GetFIO = model.User.GetFIO(); if (m_GetFIO != null) { sb.Append(m_GetFIO); } else { sb.Append(""); //    } } else { sb.Append(""); //    } sb.Append("!\r\n  ,   :\r\n\r\n ..."); //   \\  .. return sb.ToString(); } } 


もちろん、コヌドは十分に長くなりたすが、誰が芋るのでしょうか 䞀般に、このオプションは、匿名型でない堎合に最適です。 䞊蚘のコヌドでは、モデル倉数Model model = inputを<>ずしお宣蚀できたせんでした。 モデルタむプに名前がなかった堎合。

したがっお、3番目のオプションは残りたす。 同じコヌドをバむトコヌドで盎接生成したす。 テンプレヌト゚ンゞンを䜜成するずき、私自身が初めお動的関数ずバむトコヌドゞェネレヌタヌを䜿甚したした。これが、この技術を習埗する際に、芪愛なる読者の方が問題が少なくなるようにこの蚘事を曞くきっかけになりたした。

動的アセンブリ、動的関数、およびバむトコヌドゞェネレヌタヌはSystem.Reflection.Emit名前空間に蚘述されおおり、それらを䜿甚するために远加のラむブラリを接続する必芁はありたせん。

最も単玔な動的関数は次のように䜜成されたす。

 //    var genMethod = new DynamicMethod("< >", typeof(< >), new Type[] { typeof(<  1>), typeof(<  2>), ..., typeof(<  N>) }, true); //   - (  IL-  CIL-) var cs = genMethod.GetILGenerator(); //    // ... //   cs.Emit(OpCodes.Ret); //     return genMethod.CreateDelegate(typeof(< >)) as < >; 


cs.EmitOpCodes.Ret; -これは、コマンドをバむトコヌドで曞き蟌む操䜜です。 知らない人は誰でも、 バむトコヌドは.NET蚀語のアセンブラヌのようなものです。

匷さを集めおこの段萜の前の蚘事を読んだ堎合、質問がありたす。コマンドがわからない堎合はどうすればバむトコヌドを生成できたすか 答えは非垞に簡単です。 これを行うには、远加のプログラムが必芁ですプロゞェクトはアヌカむブ内にありたす。そのコヌドはネタバレの䞋で提䟛されたす。

ILDasm
 using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; namespace ILDasm { class Program { #region Static static void Main(string[] args) { new Program().Run(); } #endregion public void Run() { string basePath = AppDomain.CurrentDomain.BaseDirectory; string exeName = Path.Combine(basePath, AppDomain.CurrentDomain.FriendlyName.Replace(".vshost", "")); Process.Start(@"C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools\x64\ildasm.exe", string.Format(@"/item:ILDasm.TestClass::DoIt ""{0}"" /text /output:code.il", exeName)); } } public class TestClass { public string DoIt(object value) { StringBuilder sb = new StringBuilder(); return sb.ToString(); } } } 



プログラムの意味は、スタゞオに組み蟌たれたildasm逆アセンブラヌを実行し、 TestClassクラスのDoIt関数に察しお蚭定するこずです。 この関数の本䜓のバむトコヌドはcode.ilファむルに配眮され、開いお分析できたす。 DoIt関数のバむトコヌドを持ち蟌みたす远加削陀

 IL_0000: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor() IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: callvirt instance string [mscorlib]System.Object::ToString() IL_000c: ret 


詊行錯誀の方法ず組み合わされた頭蓋骚ボックス内の物質は、類掚によっおコヌドを生成するのに圹立ちたす。 生成された関数に必芁なもののようにDoIt関数の本䜓を蚘述し、ナヌティリティを実行し、コヌドを芋おゞェネレヌタヌに実装したす。

バむトコヌドの䞀般情報


すべおがスタック䞊に構築されたす。
加算挔算aずbを実行する堎合、倉数aの倀をスタックにプッシュし、倉数bの倀をスタックにプッシュしおから、addコマンドを呌び出す必芁がありたす。 この堎合、スタックのaずbがクリアされ、加算の結果が最䞊郚に配眮されたす。 この埌、合蚈にcを乗算し、その倀をスタックに眮き珟圚、すでに合蚈a + bがあるこずに泚意しおください、乗算挔算mulを呌び出したす。

結果のバむトコヌド

 IL_0000: ldarg.1 IL_0001: ldarg.2 IL_0002: add IL_0003: ldarg.3 IL_0004: mul 


そしお、Cでの衚瀺は次のずおりです。

 cs.Emit(OpCodes.Ldarg_1); cs.Emit(OpCodes.Ldarg_2); cs.Emit(OpCodes.Add); cs.Emit(OpCodes.Ldarg_3); cs.Emit(OpCodes.Mul); 


メ゜ッドずコンストラクタヌは同じ方法で呌び出されたす匕数をスタックに眮き、メ゜ッド/コンストラクタヌを呌び出したす。 この堎合、非静的メ゜ッドの堎合、最初にスタックに配眮するのは、メ゜ッドを呌び出しおいるクラスのむンスタンスです。

この蚘事は、バむトコヌド生成の完党なトレヌニングを提䟛するこずを目的ずしおいないため、ゞェネレヌタヌの説明を続けたしょう。

ゞェネレヌタヌコアは、そのむンタヌフェむスを実装する関数 IBuilderGenerator で囲たれおいたす。

GenerateBuilder
 /// <summary> ///    /// </summary> /// <param name="pattern">   </param> /// <param name="modelType">   </param> /// <returns></returns> public virtual BUILDER GenerateBuilder(List<PatternElement> pattern, Type modelType) { if (modelType == null) { //   ,     StringBuilder sb = new StringBuilder(); foreach (PatternElement item in pattern) { string nullValue = item.GetNullValue(); if (nullValue != null) { sb.Append(nullValue); } } string value = sb.ToString(); return (m) => value; } else { //      string methodName = "Generate_" + Guid.NewGuid().ToString().Replace("-", ""); //    var genMethod = new DynamicMethod(methodName, typeof(string), new Type[] { typeof(object) }, true); //    var cs = genMethod.GetILGenerator(); var sb = cs.DeclareLocal(typeof(StringBuilder)); var m = cs.DeclareLocal(modelType); ReflectionBuilderGeneratorContext context = new ReflectionBuilderGeneratorContext { Generator = cs, ModelType = modelType, VarSB = sb, VarModel = m, }; //   cs.Emit(OpCodes.Ldarg_0); cs.Emit(OpCodes.Isinst, modelType); cs.Emit(OpCodes.Stloc, m); //  StringBuilder      cs.Emit(OpCodes.Ldc_I4, pattern.Sum(e => e.EstimatedLength)); cs.Emit(OpCodes.Newobj, typeof(StringBuilder).GetConstructor(new Type[] { typeof(int) })); cs.Emit(OpCodes.Stloc, sb); foreach (PatternElement item in pattern) { MethodInfo processor; if (_dicProcessors.TryGetValue(item.GetType(), out processor)) { //   processor.Invoke(processor.IsStatic ? null : this, new object[] { context, item }); } } cs.Emit(OpCodes.Ldloc, sb); cs.Emit(OpCodes.Callvirt, typeof(object).GetMethod("ToString", Type.EmptyTypes)); cs.Emit(OpCodes.Ret); return genMethod.CreateDelegate(typeof(BUILDER)) as BUILDER; } } 



これが、テンプレヌト芁玠のGetNullValueメ゜ッドずEstimatedLengthプロパティが䟿利な堎所です。

ゞェネレヌタヌの特城は、その拡匵性です。 冒頭で説明したテンプレヌト芁玠のタむプ文字列定数ず匏には関係ありたせん。 必芁に応じお、独自の芁玠を考え出し、このゞェネレヌタヌを継承しお、䜜成した芁玠のタむプのバむトコヌドを生成する機胜を远加できたす。 これを行うには、コヌドでPatternElementAttribute属性を䜿甚しお関数を蚘述する必芁がありたす。たずえば、暙準ゞェネレヌタヌの実装に含たれる文字列定数のコヌド生成は次のように蚘述されたす。

 [PatternElement(typeof(StringConstantElement))] protected virtual void GenerateStringConstantIL(ReflectionBuilderGeneratorContext context, StringConstantElement element) { if (element.Value != null) { WriteSB_Constant(context, element.Value); } } /// <summary> ///   StringBuilder   /// </summary> /// <param name="context">   </param> /// <param name="value">  </param> protected virtual void WriteSB_Constant(ReflectionBuilderGeneratorContext context, string value) { if (value != null) { var cs = context.Generator; cs.Emit(OpCodes.Ldloc, context.VarSB); cs.Emit(OpCodes.Ldstr, value); cs.Emit(OpCodes.Callvirt, _dicStringBuilderAppend[typeof(string)]); cs.Emit(OpCodes.Pop); } } 


他のメ゜ッドのコヌドは提䟛したせん ずおも面倒ですが、質問があれば、個別に答えようずしたす。

性胜詊隓



なぜなら テンプレヌト゚ンゞンを他のテンプレヌト゚ンゞンず比范する機䌚はありたせん。string.Replaceに基づくハヌドコヌドテンプレヌトゞェネレヌタヌず比范したす。

テスト機胜コヌド
 /// <summary> ///   /// </summary> private void Run() { //   string outputPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Result"); if (!Directory.Exists(outputPath)) Directory.CreateDirectory(outputPath); Random rnd = new Random(0); Stopwatch sw = new Stopwatch(); //   string pattern = GetPattern(); //   string text; double patternTotal = 0; //      () double patternInitialization; //  () double patternFirst = 0; //   () double manualTotal = 0; //      () //   sw.Restart(); Patternizator patternizator = new Patternizator(pattern); sw.Stop(); patternInitialization = sw.Elapsed.TotalMilliseconds; Console.WriteLine(" {0} (v. {1})", patternizator.GetType().Assembly.GetName().Name, patternizator.GetType().Assembly.GetName().Version); //     for (int i = 0; i < COUNT_PATTERNIZATOR; i++) { //   User user = new User { Surname = RandomElement(rnd, SURNAMES), Name = RandomElement(rnd, NAMES), Patronymic = RandomElement(rnd, PATRONYMICS), //   1950 - 1990  Birthdate = new DateTime(1950, 1, 1).AddDays(rnd.NextDouble() * 40.0 * 365.25) }; var model = new { User = user, UserName = user.Name, Now = DateTime.Now, }; //     sw.Restart(); text = patternizator.Generate(model); sw.Stop(); patternTotal += sw.Elapsed.TotalMilliseconds; if (i == 0) { patternFirst = sw.Elapsed.TotalMilliseconds; } //     if (i < COUNT_MANUAL) { // !             //   -      Replace    sw.Restart(); { StringBuilder sb = new StringBuilder(pattern); DateTime now = DateTime.Now; sb.Replace("{User.GetFIO()|}", model.User.GetFIO() ?? ""); sb.Replace("{UserName|}", model.UserName ?? ""); sb.Replace("{User.Age:0}", model.User.Age.ToString("0")); sb.Replace("{Now:dd MMMM yyyy}", now.ToString("dd MMMM yyyy")); sb.Replace("{Now:HH:mm:ss}", now.ToString("HH:mm:ss")); text = sb.ToString(); } sw.Stop(); manualTotal += sw.Elapsed.TotalMilliseconds; } } WriteHeader(""); WriteElapsedTime(" ", patternInitialization); WriteElapsedTime("  ", patternFirst); Console.WriteLine(); WriteElapsedTime(string.Format("    {0} ", COUNT_PATTERNIZATOR), patternTotal); WriteElapsedTime("   ", patternTotal / COUNT_PATTERNIZATOR); WriteHeader(" ()"); WriteElapsedTime(string.Format("    {0} ", COUNT_MANUAL), manualTotal); WriteElapsedTime("   ", manualTotal / COUNT_MANUAL); Console.WriteLine(); Console.WriteLine("    ..."); Console.ReadKey(); } 



スクリヌンショット


結論の代わりに



この蚘事は倧きく、倚すぎるかもしれないこずがわかったが、そこで取り䞊げられたトピックはそれほど単玔ではない。したがっお、バむトコヌド生成のどの質問をより詳现に知りたいのか、賌読を停止するようお願いしたす。これらの問題に぀いおは、別の蚘事を曞くようにしたす。

私の他の出版物



  1. 関数むンタヌプリタヌを䜿甚した.NET䞊のプロゞェクトのロヌカラむズ

Source: https://habr.com/ru/post/J251765/


All Articles