2014-03-30 120 views
0

如何使用約定自動從接口創建類的默認實現。換句話說,如果我有一個接口:使用字段/屬性命名約定從接口自動生成類生成

public interface ISample 
{ 
    int SampleID {get; set;} 
    string SampleName {get; set;} 
} 

是否有一個片段,T4模板,或者從上面的接口自動生成下面類中的一些其他方法?正如你所看到的,我希望該字段的名稱前把下劃線,然後使該領域的相同的名稱屬性,但較低的情況下,第一個字母:

public class Sample 
{ 
    private int _sampleID; 
    public int SampleID 
    { 
      get { return _sampleID;} 
      set { _sampleID = value; } 
    } 

    private string _sampleName; 
    public string SampleName 
    { 
      get { return _sampleName;} 
      set { _sampleName = value; } 
    }  
} 

回答

1

我不知道,如果T4在可讀性方面是最簡單的解決方案,但您也可以使用其他代碼生成工具:the CodeDom provider

這個概念非常簡單:代碼由構建在一起的構建塊組成。

當時間成熟時,這些構建塊將被解析爲選擇的語言。你最終得到的是一個包含你新創建的程序源代碼的字符串。之後,您可以將其寫入文本文件以供進一步使用。

正如您已經注意到的那樣:沒有編譯時結果,一切都是運行時。如果你真的想要編譯時,那麼你應該使用T4代替。

的代碼:

using System; 
using System.CodeDom; 
using System.CodeDom.Compiler; 
using System.IO; 
using System.Reflection; 
using System.Text; 

namespace TTTTTest 
{ 
    internal class Program 
    { 
     private static void Main(string[] args) 
     { 
      new Program(); 
     } 

     public Program() 
     { 
      // Create namespace 
      var myNs = new CodeNamespace("MyNamespace"); 
      myNs.Imports.AddRange(new[] 
      { 
       new CodeNamespaceImport("System"), 
       new CodeNamespaceImport("System.Text") 
      }); 

      // Create class 
      var myClass = new CodeTypeDeclaration("MyClass") 
      { 
       TypeAttributes = TypeAttributes.Public 
      }; 

      // Add properties to class 
      var interfaceToUse = typeof (ISample); 
      foreach (var prop in interfaceToUse.GetProperties()) 
      { 
       ImplementProperties(ref myClass, prop); 
      } 

      // Add class to namespace 
      myNs.Types.Add(myClass); 

      Console.WriteLine(GenerateCode(myNs)); 
      Console.ReadKey(); 
     } 

     private string GenerateCode(CodeNamespace ns) 
     { 
      var options = new CodeGeneratorOptions 
      { 
       BracingStyle = "C", 
       IndentString = " ", 
       BlankLinesBetweenMembers = false 
      }; 

      var sb = new StringBuilder(); 
      using (var writer = new StringWriter(sb)) 
      { 
       CodeDomProvider.CreateProvider("C#").GenerateCodeFromNamespace(ns, writer, options); 
      } 

      return sb.ToString(); 
     } 

     private void ImplementProperties(ref CodeTypeDeclaration myClass, PropertyInfo property) 
     { 
      // Add private backing field 
      var backingField = new CodeMemberField(property.PropertyType, GetBackingFieldName(property.Name)) 
      { 
       Attributes = MemberAttributes.Private 
      }; 

      // Add new property 
      var newProperty = new CodeMemberProperty 
      { 
       Attributes = MemberAttributes.Public | MemberAttributes.Final, 
       Type = new CodeTypeReference(property.PropertyType), 
       Name = property.Name 
      }; 

      // Get reference to backing field 
      var backingRef = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), backingField.Name); 

      // Add statement to getter 
      newProperty.GetStatements.Add(new CodeMethodReturnStatement(backingRef)); 

      // Add statement to setter 
      newProperty.SetStatements.Add(
       new CodeAssignStatement(
        new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), backingField.Name), 
        new CodePropertySetValueReferenceExpression())); 

      // Add members to class 
      myClass.Members.Add(backingField); 
      myClass.Members.Add(newProperty); 
     } 

     private string GetBackingFieldName(string name) 
     { 
      return "_" + name.Substring(0, 1).ToLower() + name.Substring(1); 
     } 
    } 

    internal interface ISample 
    { 
     int SampleID { get; set; } 
     string SampleName { get; set; } 
    } 
} 

這產生:

enter image description here

壯麗,是嗎?

旁註:屬性給出Attributes = MemberAttributes.Public | MemberAttributes.Final,因爲省略MemberAttributes.Final將使其成爲virtual

最後但並非最不重要的是:這種迷人的靈感。 Metaprogramming in .NET by Kevin Hazzard and Jason Bock, Manning Publications.