kazuk は null に触れてしまった

C# / .NET 系技術ネタ縛りでお送りしております

T4からの脱獄キットリリースしました


つい先日、「良く訓練されたT4使いは…」なんて事を書いてたりしましたが…
 
絶望していた ToStringHelper に死亡宣告を与える事に成功しましたので公開致します。
 
 
ReadMe.txt をべたっと貼り付け。英語の添削できるひとの突っ込み希望です。
 
ReadMe.txt に書いてある事ですけど、やっている事は T4 が作る .cs の中で ToStringHelper に対してもっとマットウな実装を作ってぶち当てる。
.cs に書いてあるコードと、参照アセンブリにあるコードでは .cs に書いてある方が優先されて使われるので結果としてToStringHelperのアホさかげんに付き合う必要なんてナッシングって事です。
 
 
	this is a readme for using T4 Jail Braking Kit.
	(this readme is Generated by ReadMe.tt on this folder)

1.	What Is Jail.

	T4 is output to <#=value #> to 
	
	in TextTemplatingFileGenerator

		Write( Microsoft.VisualStudio.TextTemplating.ToStringHelper.ToStringWithCulture( value ) )

		oops, static method call, can't be change to anything.

	in TextTemplatingFilePreprocessor

		Write( ToStringHelper.ToStringWithCulture( value ) )

	this ToStringHelper.ToStringWithCulture is so STUPID and anything to do replace.
	( ToStringHelper not partial and,ToStringWithCulture is not virtual )

		path the null, throws ArgumentNullException, I write code!, write be "null"!
		path the boolean, "True", "False", I don't know VB.
		path the List, OK, you recommend <#@ output extension=".il" #>!

	I do everytime grep and replace 'Write( ToStringHelper.ToStringWithCulture(' to runs 
	after TextTemplatingFilePreprocessor, to runs after, after,

	In result, I do brake the Jail, now!

2.	Detail of Jail Brake.

2.1.	in TextTemplatingFileGenerator,

	 Kill the stupid ToStringHelper with type definition confrict, 
	in type definition confircted , C# is prior .cs defined as referenced assembly defined.
	 That's raise warning, but success fully compiled and run.

	 '<#+ code #>' contexted, namespace with 'Microsoft.VisualStudio.TextTemplating(random value)'
	and TextTransformation class.

	<#+
	} // exit class
	} // exit namespace

	namespace Microsoft.VisualStudio.TextTemplating // target stay in close!, ready to fire...
	{
		pubic partial class ToStringHelper // Fire!, All belettes to be use! collectry kill that!
		{
			// implement the more smart guy

	but, i can't return original context because namespace is named by rundom value,
	T4 is last of '<#+' and '#>' renders '}' for exit class,'}' for exit namespace.
	
	In result, i stay in my ToStringHelper class contxt, T4 is closing by default,
	and can be exetend to my ToStringHelper implementation by after include .tt's.

	( dont be afraid, your tt's <#+ #> section is renders out after complete
	TextTransformation ,see Appendix B)

	Jail Braking require's only 2 line ,write below  in last of your tt's head 
	of directivs (see this file's top)

	<#@ include file="BrakeJail.ttlib" #>
	<# Microsoft.VisualStudio.TextTemplating.ToStringHelper.Initialize(this); #>
	
	 i implements ToStringHelper for you, this is extensible and having 
	Dictionary> your custom type formatters to add it.
	 this implementation is little bit complex, 
	
	because 
	  avoid boxing. if you int formatter added, int value was used by just
	 int, no any type casting and not be place to object.
 	  reflection result cashe implemented transparently a expression compiled 
	 result cache,
	
	perfomance is justice!

	usage in example 
	on head of file <#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
	<# ToStringHelper.AddFormatter( (bool b)=>b?"true":"false" ); #>
	<#= 0==1 #>

	result:
		false

	second choice, you handle event of ToStringHelper.ResolveFormatter

	this choice can be to do that, if type is array, create array formatter etc...

	in example, List formatter is shown below:

	<#  
		EventHandler handler =
		(src, e) =>
		{
			Type t = e.TypeForResolveFormatter;
			if( t.IsGenericType && t.GetGenericTypeDefinition()==typeof(List<>) )
			{
				e.SetFormatter( t, GetListFormatter(t) );
			}
		};

		ToStringHelper.ResolveFormatter+=handler;
	#>
	and bottom of this file implement it:
	<#+
		public static string ListFormatter( List list )
		{
			return "new List<"+typeof(T).FullName+"> {" +
					string.Join( ",", (from item in list 
									select ToStringHelper.ToStringWithCulture( item ) ) )
					+ "}" ;
		}

		public Delegate GetListFormatter( Type listType )
		{
			Type[] genArgs = listType.GetGenericArguments();
			var mi = this.GetType()
				.GetMethods()
				.Single( m=>m.Name=="ListFormatter" )
				.MakeGenericMethod( genArgs[0] );
			var listParam = Expression.Parameter( listType, "list" );
			var exp = Expression.Lambda( 
				Expression.Call( mi, listParam ),
				listParam );
			return exp.Compile();
		}
	#>
	
	I do it:
	<#= new List { 1,2,3 }#>
	result:
	new List {1,2,3}

	Note:
		reflection to Generic Methods pattern.

		 method info of Method 
			-> MakeGenericMethod 
				-> Exp.Lambda( exp.Call )
					-> .Compile()
						-> .Invoke()

		 is standard of todays reflection to Generic Methods pattern.

		
		Delegate is sometime used Unknown type Lambda in code.

		if you use Delegate.Method.GetParameters()
		index 0 was sometime not real parameter thats ParameterType is
			System.Runtime.CompilerServices.ExecutionScope (in mscorlib 2.0),
			System.Runtime.CompilerServices.Closure (is mscorlib 4.0), 
		
		in Delegate.Target==null
			GetParameters results parameters from index 0.
		in Delegate.Target!=null
			GetParameters results parameters from index 1,
				index 0 is Target's type.
		


2.2	in TextTemplatingFilePreprocessor

	I writing code now! may be as soon complete and show it.

----------------
Appendix. A. 
	My .tt nameing rule.

	.tt		designed for run with CustomTool 
	.ttinc	include file contains '<# #>' or else normal text.
			because output is created <#@ include #> only.
	.ttlib	include file but only contents '<#+ #>' sections 
	        because no output <#@ include #> only.

	Note : T4's rule of .tt and included files output ordering
		(not MS specified and no source, 
		 this is only my trying some tests and results seems to be it.
		 if MS specified it, send link to @kazuk on twitter)

	example:

	<#@ include filename="file1"#>
	<#@ include filename="file2"#>
	<#@ include filename="file3"#>
	my tt contents of normal text and '<# #>' sections
	my tt <#+ #> sections

	output does below order.

	1. nomal text and '<# #>' sections from file1
	2. nomal text and '<# #>' sections from file2
	3. nomal text and '<# #>' sections from file3
	4. my tt contents of normal text and '<# #>' sections
	5. my tt <#+ #> sections
	6. <#+ #> sections from file1
	7. <#+ #> sections from file2
	8. <#+ #> sections from file3

Appendix B.
	solution for 'Only one chance for override Dispose(bool) problem'

	on your include list
	file1.ttlib  override Dispose(bool) and appeed expencive object property for tt
	file2.ttlib  override Dispose(bool) and appeed expencive object property for tt 
	... thats can't be

	If you trade me the 'only on chance for override Dispose(bool)'
	include 'DisposeImplement.ttlib' on this archive Utilities folder.

	this has two protected field 
	IList ExpenciveObjects 
	IList> ExpenciveObjectRetrievers

	and override Dispose(bool) run's Dispose call of all ExpenciveObjects 
	and call's ExpenciveObjectRetrievers,
	 if Func returns non null, call the Dispose to it.

	your tt's add object to ExpenciveObjects or 
	ExpenciveObjectRetrievers.Add( () => this.MyProperty )

	Note:

	I know 'only one chance for implement class initializer problem' and
	'only one chance for implement defalut constructor problem' ,
	'only one chance for implement override Initialize problem' but i dou't have
	utilizable solution for it. let me it.

	( I will be implement 'override Initialize' in this Jail Brake kit,
	but this is only one chance and by case broke your tt's, I do not it,
	remember call Initialize method on my ToStringHelper on your file head!!)

 
広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。