【.net 深呼吸】細說CodeDom(4):類型定義

 

上一篇文章中說了命名空間,你猜猜接下來該說啥。是了,命名空間下面就是類型,知道了如何生成命名空間的定義代碼,以後就該學會如何聲明類型了。c#

CLR的類型一般有這麼幾種:類、接口、結構、枚舉、委託。是這麼幾個,應該沒有漏掉的吧。ide

定義類型,除了委託外均可以用 CodeTypeDeclaration 類完成。CodeNamespace類公開一個Types集合,定義的類型必須添加到這個集合中,才能與命名空間關聯。spa

舉個例子,下面代碼將定義一個叫 Mouse 的類。code

            // 編譯單元
            CodeCompileUnit unit = new CodeCompileUnit();
            // 命名空間
            CodeNamespace nspace = new CodeNamespace();
            nspace.Name = "Sample";
            unit.Namespaces.Add(nspace);
            // 類聲明
            CodeTypeDeclaration clsdcl = new CodeTypeDeclaration();
            clsdcl.Name = "Mouse";
            // 公共且密封,不讓繼承
            clsdcl.TypeAttributes = System.Reflection.TypeAttributes.Sealed | System.Reflection.TypeAttributes.Public;
            // 加入類型集合中
            nspace.Types.Add(clsdcl);

            CodeDomProvider prd = CodeDomProvider.CreateProvider("cs");
            prd.GenerateCodeFromCompileUnit(unit, Console.Out, null);

不須要顯式把 IsClass 說罷爲 true,由於默認就是生成類(class)的。這裏有一點老周要說明一下。對象

描述類型的可訪問性有兩個屬性能夠用,一個是從 CodeTypeMember 類繼承的 Attributes,因爲MemberAttributes枚舉不能進行組合運用(我想把類定義爲 public sealed),因而我就選用了TypeAttributes,它用的值是反射裏面的TypeAttributes枚舉,這個枚舉能夠多個值組合運用。blog

生成的代碼以下圖所示。繼承

 

下面代碼將定義一個名爲 Point 的結構。接口

            CodeTypeDeclaration strdcl = new CodeTypeDeclaration("Point");
            strdcl.IsStruct = true;
            strdcl.TypeAttributes = System.Reflection.TypeAttributes.NotPublic;
            nspace.Types.Add(strdcl);

NotPublic表示類型可訪問性爲internal,即僅限當前程序集可見。生成代碼以下圖所示。字符串

 

知道怎麼定義類和結構後,那麼枚舉就難不倒你了。string

            CodeTypeDeclaration endcl = new CodeTypeDeclaration("AccessType");
            endcl.IsEnum = true;
            nspace.Types.Add(endcl);

生成代碼以下。

 

可是,大夥伴們必定會問,那委託呢。CodeTypeDeclaration類並不能用來聲明委託類型,但它派生出了一個CodeTypeDelegate類,它是定義委託類型專業戶。

咱們知道,委託相似於方法,那麼你想一想,委託類型須要幾個要素。首先,參數列表要吧。而後,還得有個返回值。好,仍是實際效果有用,很少說,來,先定義一個委託類型試試手。

            CodeCompileUnit unit = new CodeCompileUnit();
            CodeNamespace ns = new CodeNamespace("Calculators");
            unit.Namespaces.Add(ns);

            CodeTypeDelegate dl = new CodeTypeDelegate("OnAdd");
            // 返值爲int類型
            dl.ReturnType = new CodeTypeReference(typeof(int));
            // 兩個參數
            CodeParameterDeclarationExpression p1 = new CodeParameterDeclarationExpression();
            p1.Name = "x";
            p1.Type = new CodeTypeReference(typeof(int));
            CodeParameterDeclarationExpression p2 = new CodeParameterDeclarationExpression();
            p2.Name = "y";
            p2.Type = new CodeTypeReference(typeof(int));
            dl.Parameters.Add(p1);
            dl.Parameters.Add(p2);
            ns.Types.Add(dl);

CodeTypeReference用於生成類型引用代碼,能夠用Type對象提供類型信息,也能夠用字符串來提供。方法參數能夠用CodeParameterDeclarationExpression類來聲明,核心元素是參數類型與參數名。

若是委託沒有參數,就不用向Parameters集合添加任何東西,有幾個參數就加幾個。

最後生成的委託類型代碼以下。

 

下面代碼生成一個無參數並返回void的委託類型。

            CodeTypeDelegate dl2 = new CodeTypeDelegate("DoWork");
            dl2.ReturnType = new CodeTypeReference(typeof(void));
            ns.Types.Add(dl2);

生成的委託類型爲

 

============================================

此時你們可能會想到,類型之間存在繼承關係,好比類與類之間,類/結構能夠實現接口。

下面代碼聲明瞭兩個類——A、B,其中B從A派生。

            CodeCompileUnit unit = new CodeCompileUnit();
            CodeNamespace ns = new CodeNamespace("Samples");
            unit.Namespaces.Add(ns);

            CodeTypeDeclaration t1 = new CodeTypeDeclaration("A");
            CodeTypeDeclaration t2 = new CodeTypeDeclaration("B");
            // B 從 A 派生
            t2.BaseTypes.Add(new CodeTypeReference(t1.Name));
            ns.Types.AddRange(new CodeTypeDeclaration[] { t1, t2 });

CodeTypeDeclaration 類有一個 BaseTypes 集合,用來設置該類型的基類。

這裏你必須一個問題:雖然 BaseTypes 是一個集合,能夠添加N個類型引用,但是你得遵照.NET的面向對象規則,即類不能多繼承,但類型能夠實現多個接口。

儘管你能夠這麼搞:

            t2.BaseTypes.Add(new CodeTypeReference(t1.Name));
            t2.BaseTypes.Add(new CodeTypeReference(typeof(string)));

而後還生成了這樣的代碼

然而,你就要想想了,這樣的代碼可否經過編譯,不興趣的朋友不妨試試。

想試試嗎,好,老周就獻醜了,列位看官莫笑。

            CodeDomProvider prd = CodeDomProvider.CreateProvider("c#");
            // 生成代碼
            prd.GenerateCodeFromCompileUnit(unit, Console.Out, null);
            CompilerParameters options = new CompilerParameters();
            // 輸出文件
            options.OutputAssembly = "test.dll";
            // 引用的程序集
            options.ReferencedAssemblies.Add("System.dll");
            // 開始編譯
            CompilerResults res = prd.CompileAssemblyFromDom(options, unit);

            // 200% 出錯
            if (res.Errors.Count == 0)
            {
                Console.WriteLine("編譯完成。");
                Console.WriteLine($"輸出程序集:{res.CompiledAssembly.Location}");
            }
            else
            {
                foreach (CompilerError er in res.Errors)
                {
                    Console.WriteLine($"{er.ErrorNumber} - {er.ErrorText}");
                }
            }

而後,一編譯,就有好戲看了。

這告訴你,類A的基類不能有多個類。

 

下面再看看如何實現多個接口。

            CodeCompileUnit unit = new CodeCompileUnit();
            CodeNamespace ns = new CodeNamespace("Commons");
            unit.Namespaces.Add(ns);

            // 這兩個都是接口
            CodeTypeDeclaration t1 = new CodeTypeDeclaration("IBall");
            t1.IsInterface = true;
            CodeTypeDeclaration t2 = new CodeTypeDeclaration("IPlayer");
            t2.IsInterface = true;
            ns.Types.Add(t1);
            ns.Types.Add(t2);

            // 這個是類,實現上面兩個接口
            CodeTypeDeclaration t3 = new CodeTypeDeclaration("FootballPlayer");
            t3.BaseTypes.Add(new CodeTypeReference(t1.Name));
            t3.BaseTypes.Add(new CodeTypeReference(t2.Name));
            ns.Types.Add(t3);

t1和t2都是接口類型,t3是類,它將實現前面兩個接口。運行後會生成如下代碼。

 

 OK,對於類型定義,今天就講這麼多吧。順着這個層次,下一篇文章,老周就會說說類型成員的定義。

開飯了,開飯了……

相關文章
相關標籤/搜索