2014年1月19日 星期日

C# - Reflection 反映

反映 (Reflection) 會提供 Type 型別的物件,用來描述組件、模組和型別。 您可以使用反映來動態建立型別的執行個體、將型別繫結至現有物件,或從現有物件取得型別,並叫用其方法或存取其欄位和屬性。 如果您在程式碼中使用屬性,反映可讓您存取這些屬性。 如需詳細資訊,請參閱使用屬性擴充中繼資料。

反射提供了封裝程式集、模組和類型的物件。
開發者可以使用反射動態地創建類型的實例,將類型綁定到現有物件(這個不會),或從現有物件中獲取類型。然後,可以調用類型的方法或訪問其欄位和屬性。

namespace TestReflection
{
    class Program
    {
        static void Main(string[] args)
        {
            object A = new AX();
            object B = new AXzhz();
            new TestObjectType().TestObjectTypeNow(A, B);
        }
    }

    class AX { }
    class AXzhz { }
    class TestObjectType
    {
        internal void TestObjectTypeNow(object A, object B)
        {
            Type tpA = A.GetType();
            Type tpB = B.GetType();
            Console.WriteLine(tpA.FullName);
            Console.WriteLine(tpB.FullName);
            Console.ReadLine();
        }
    }

}

獲得的Type實例的用途:

  • 獲得類名:如上面例子的FullName屬性,返回TestReflection.AX

這個也比較噁心,直接用A.ToString();返回的也是這個結果.

  • 創建該類的對象.你首先通過ⅰ來獲得類名AX

AX ax = (AX)Activator.CreateInstance(tpA);

  • 獲得物件所屬類的相關資訊

通過tpA的相關屬性,來得到該類的相關資訊.
通過一個物件實例,可以獲得包含這個物件實例的類的Assembly,進而獲得整個Assembly的資訊。

namespace TestReflection
{
    class Program
    {
        public static void Main(string[] args)
        {
            object A = new AX();

            //獲取物件所屬的Assembly的所有類的基本資訊
            new TestObjectType().TestObjectTypeNow(A);
        }
    }

    class AX
    {
        internal int kkkkkkkk = 0;
        public int ooooooooo;
        private int property;

        public int Property
        {
            get { return property; }
            set { property = value; }
        }

        public void A()
        {
            Console.WriteLine("AX's function!~");
        }
    }

    class AXzhz { }

    class TestObjectType
    {
        internal void TestObjectTypeNow(object A)
        {
            Type tpA = A.GetType();
            Assembly assembly = tpA.Assembly;
            Type[] types = assembly.GetTypes();
            foreach (Type type in types)
            {
                Console.WriteLine("【類名】" + type.FullName);
                ConstructorInfo[] myconstructors = type.GetConstructors();
                Show(myconstructors);
                FieldInfo[] myfields = type.GetFields();
                Show(myfields);
                MethodInfo[] myMethodInfo = type.GetMethods();
                Show(myMethodInfo);
                PropertyInfo[] myproperties = type.GetProperties();
                Show(myproperties);
            }

            Console.ReadLine();
        }

        //顯示陣列的基本資訊
        public void Show(object[] os)
        {
            foreach (object var in os)
            {
                Console.WriteLine(var.ToString());
            }

            Console.WriteLine("----------------------------------");
        }
    }

}

實現抽象工廠的基礎,也是實現抽象工廠的核心技術,通過它,可以動態創建一個你想要的物件。下面的例子是演示如何動態創建ChineseName或EnglishName的實例:


namespace TestReflection
{
    class AXzhz_sReflectionExample
    {
        public static void Main()
        {
            IName name = AbstractFactory.GetName();
            name.ShowName();
        }
    }

    public class AbstractFactory
    {
        public static IName GetName()
        {
            //s的值以後從Web.config動態獲取
            //s賦值為:TestReflection.EnglishName,將顯示英文名
            string s = "TestReflection.ChineseName";
            IName name = (IName)Assembly.Load("TestReflection").CreateInstance(s);
            return name;
        }
    }

    //聲明一個介面,它有一個顯示"名字"的功能
    public interface IName
    {
        void ShowName();
    }

    //實現介面,顯示中國名字
    public class ChineseName : IName
    {
        public void ShowName()
        {
            Console.WriteLine("我叫AX!");
            Console.ReadLine();
        }
    }

    //實現介面,顯示英國名字
    public class EnglishName : IName
    {
        void IName.ShowName()
        {
            Console.WriteLine("My name is AXzhz!");
            Console.ReadLine();
        }
    }

}


可以使用下面的方法了解都用到了哪些Assembly,得到Assembly裡的資訊:

namespace TestReflection
{
    class ShowAllAssembly
    {
        public static void Main()
        {
            //獲得解決方案的所有Assembly
            Assembly[] AX = AppDomain.CurrentDomain.GetAssemblies();

            //遍歷顯示每個Assembly的名字
            foreach (object var in AX)
            {
                Console.WriteLine("Assembly的名字:" + var.ToString());
            }

            //使用一個已知的Assembly名稱,來創建一個Assembly
            //通過CodeBase屬性顯示最初指定的程式集的位置
            Console.WriteLine("最初指定的程式集TestReflection的位置:" + Assembly.Load("TestReflection").CodeBase);
            Console.ReadLine();
        }
    }
}



打完收工

-雲遊山水為知已逍遙一生而忘齡- 電腦神手

2014年1月18日 星期六

C# - Iterator反覆運算器

在Design Pattern中,Iterator Pattern相信大家都很熟悉,而在C#中,使用foreach語法本身就可以算是在使用Iterator Pattern了。在.Net支援的資料結構裡,大部份都會支援foreach語法,不過有時候因為需求,會自己實作其它的資料結構,本身文章將會說明如何讓自己設計的類別支援foreach語法。

下面是微軟官方對Iterator的定義:
Iterator 是一種方法、get 存取子 (Accessor),或是使用 yield 關鍵字對陣列或集合類別執行自訂反覆運算法的運算子。yield 傳回陳述式 (Statement) 會使來源序列 (Sequence) 中的項目,在即將存取來源序列中的下一個項目時傳回到呼叫端。雖然您可以將 Iterator 撰寫為方法,編譯器 (Compiler) 卻會將其轉譯為巢狀類別,也就是實際上的狀態機器。只要用戶端程式碼上的 foreach 迴圈持續進行,這個類別就會持續追蹤 Iterator 的位置。


Iterator 的特性:
Iterator 是程式碼區段,會傳回相同型別之按順序排列的值。
Iterator 可以當做方法主體、運算子或 get 存取子使用。
Iterator 程式碼會使用 yield return 陳述式輪流傳回各元素。yield break 則會結束反覆運算。
可在類別上實作多個 Iterator。每個 Iterator 必須像任何類別成員一樣擁有唯一名稱,且可以由 foreach 陳述式中的用戶端程式碼叫用,如下所示:foreach(int x in SampleClass.Iterator2){}。
Iterator 的傳回型別必須是 IEnumerable、IEnumerator、IEnumerable<T> 或 IEnumerator<T>。
Iterators 是 LINQ 查詢中延後執行行為的基礎。
yield 關鍵字會用來指定所傳回的單一個或多個值。當到達 yield return 陳述式時,便會儲存目前的位置。下一次呼叫此 Iterator 時,便會從這個位置重新開始執行。
Iterator 特別適合與集合類別搭配使用,因為能夠提供逐一查看像是二元樹等複雜資料結構的方法。

現在假設要從資料庫撈Person資料表的資料,欄位有age跟name並且包成Person物件,Person類別的設計如下:

public class Person
{
public int age;
public string name;

public Person(){}
}

再來實作資料結構MyList,用來放Person物件。
首先引用System.Collections這個命名空間。

using System.Collections;

MyList的程式碼如下,特別注意的是MyList要繼承IEnumerable<>跟IEnumerable都不能少。


在Design Pattern中,Iterator Pattern相信大家都很熟悉,而在C#中,使用foreach語法本身就可以算是在使用Iterator Pattern了。在.Net支援的資料結構裡,大部份都會支援foreach語法,不過有時候因為需求,會自己實作其它的資料結構,本身文章將會說明如何讓自己設計的類別支援foreach語法。

現在假設要從資料庫撈Person資料表的資料,欄位有age跟name並且包成Person物件,Person類別的設計如下:


public class Person
{
public int age;
public string name;

public Person(){}
}

再來實作資料結構MyList,用來放Person物件。
首先引用System.Collections這個命名空間。

using System.Collections;

MyList的程式碼如下,特別注意的是MyList要繼承IEnumerable<>跟IEnumerable

public class MyList<T>: IEnumerable<T>, IEnumerable where T:Person
{
public MyList(int size)
{
this.MyResources = new T[size];
}

public T this[int index]
{
get
{
return this.MyResources[index];
}

set
{
this.MyResources[index] = value;
}
}

private T[] MyResources;

IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
for(int i = 0; i< this.MyResources.Count(); i++)
{
yield return this.MyResources[i];
}
}

IEnumerator IEnumerable.GetEnumerator()
{
return (IEnumerator)((IEnumerable<T>)this).GetEnumerator();
}
}

在IEnumerable<T>.GetEnumerator中,有使用到yield這個關鍵字,可以很輕易的篩選要回傳的元素,本範例為了方便說明,所以是回傳所有的元素。這是C# 2.0開始才有的。

MyList<Person>testList = new MyList<Person>(4);
testList[0] = new Person();
testList[0].age = 10;
testList[0].name = "Peter";
testList[1] = new Person();
testList[1].age = 10;
testList[1].name = "John";
testList[2] = new Person();
testList[2].age = 10;
testList[2].name = "Jobs";
testList[3] = new Person();
testList[3].age = 10;
testList[3].name = "Pages";


也可以做一些特別的篩選動作:
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
for(int i = 0; i< this.MyResources.Count(); i +=2)
{
yield return this.MyResources[i];
}

for(int i=1; i<this.MyResources.Count();i+=2)
{
yield return this.MyResources[i];
}
}

上面的方法,是讓偶數位的元素先產生,然後是奇數位的。
這樣的寫法,也可以用在LINQ上:
var result = from p in testList where p.age == 12 select p;

foreach(Person item in result)
{
Console.WriteLine("Age:{0}, Name:{1}", item.age, item.name);
}

-雲遊山水為知已逍遙一生而忘齡- 電腦神手

2014年1月16日 星期四

C# Design Pattern - Proxy Pattern 代理模式

代理模式如同字面上的意思,當想要做某件事的時候(ex:買衣服),不直接存取原始物件(直接找工廠),而是透過代理的管道(代理商)來處理。有以下主要的特點:

  • 遠端代理(Remote):代理遠端程時執行,例如可以透過WebService的WSDL定義產生中介檔的函式庫,透過這個函式庫就可以存取WebService。
  • 虛擬代理(Virtual):將需要秏費大量時間或是複雜的實體,利用代理模式的物件代替。
  • 安全代理(Protect or Access):控制物件存取時的許可權。
  • 智慧參考(Smart Reference):提供比原有物件更多的服務。


在物件導向系統中,由於有些物件創建的開銷很大,或者某些操作需要安全控制,或者需要進程外的訪問等,直接訪問會給使用者、或者系統結構帶來很多麻煩。

如何在不失去透明操作物件的同時來管理/控制這些物件特有的複雜性,增加一層間接層是軟體發展中常見的解決方式。



基本程式結構範例:
using System;
class Program
{
    static void Main(string[] args)
    {
        IService proxy = new ProxyService();
        proxy.DoWork();
    }
}

interface IService
{
    void DoWork();
}

class ProxyService : IService
{

    private RemoteService _rs;

    public ProxyService()
    {
        _rs = new RemoteService();
    }

    public void DoWork()
    {
        _rs.DoWork();
    }

}

class RemoteService : IService
{
    public void DoWork()
    {
        Console.WriteLine("Hello Proxy Pattern");

    }

}

Proxy Pattern提供一個仲介以控制對這個物件的訪問。一張支票或銀行存單是帳戶中資金的代理。支票在市場交易中用來代替現金,並提供對簽發人帳號上資金的控制。

在軟體系統 中,當存取網路上一台電腦的資源時,是正在跨越網路障礙,當去存取伺服器上資料庫時,又在跨越資料庫存取障礙和網路障礙。跨越這些障礙有時候是非常複雜的,如果t可以去關注處理這些障礙問題,可能就會忽視了本來應該關注的業務邏輯問題,Proxy模式有助於解決這些問題。以一個簡單的數學計算程式為例,這個程式只負責進行簡單的加減乘除運算:


public class Math
{

    public double Add(double x, double y)
    {
        return x + y;
    }

    public double Sub(double x, double y)
    {
        return x - y;
    }

    public double Mul(double x, double y)
    {
        return x * y;
    }

    public double Dev(double x, double y)
    {
        return x / y;
    }

}

如果說這個計算程式部署在本地電腦上,使用就非常之簡單,不用去考慮Proxy模式。但現在問題是這個Math類並沒有部署在本地,而是部署在一台伺服器上,也就是說Math類別根本和客戶程式不在同一個位址空間之內,現在要面對的是跨越Internet這樣一個網路障礙:


public class App
{
    public static void Main()
    {
        Math math = new Math();

        double addresult = math.Add(2, 3);

        double subresult = math.Sub(6, 4);

        double mulresult = math.Mul(2, 3);

        double devresult = math.Dev(2, 3);
    }

}

為了解決由於網路等障礙引起複雜性,就引出了Proxy模式,使用一個本地的代理來替Math類打點一切,即為系統引入了一層間接層,示意圖如下:


在MathProxy中對實現Math資料類的訪問,讓MathProxy來代替網路上的Math類別,這樣MathProxy就好像是本地Math類別,它與客戶程式處在了同一位址空間內:

public class MathProxy
{
    private Math math = new Math();
    public double Add(double x, double y)
    {
        return math.Add(x, y);
    }

    public double Sub(double x, double y)
    {
        return math.Sub(x, y);
    }

    public double Mul(double x, double y)
    {
        return math.Mul(x, y);
    }

    public double Dev(double x, double y)
    {
        return math.Dev(x, y);
    }

}

現在已經實現了對Math類的代理,存在的一個問題是在MathProxy類別中調用了原實現類Math的方法,但是Math並不一定實現了所有的方法,為了強迫Math類別實現所有的方法,另一方面,為了更加透明的去操作物件,在Math類和MathProxy類別的基礎上加上一層抽象,即它們都實現與IMath介面,示意圖如下:


public interface IMath

{

    double Add(double x, double y);

    double Sub(double x, double y);

    double Mul(double x, double y);

    double Dev(double x, double y);

}



Math類和MathProxy類分別實現IMath介面:



public class MathProxy : IMath { }


public class Math : IMath { }

此時我們在客戶程式中就可以像使用Math類一樣來使用MathProxy類了:

public class App

{

    public static void Main()

    {

        MathProxy proxy = new MathProxy();

        double addresult = proxy.Add(2, 3);

        double subresult = proxy.Sub(6, 4);

        double mulresult = proxy.Mul(2, 3);

        double devresult = proxy.Dev(2, 3);

    }

}

到這兒整個使用Proxy模式的過程就完成了,這樣的解決方案,無非是在客戶程式和Math類之間加了一個間接層,這是比較常見的解決問題的方法。另外,對於程式中的介面Imath,並不是必須的,大多數情況下,為了保持對物件操作的透明性,並強制實現類別實現代理類所要調用的所有的方法,會讓它們實現與同一個介面。

代理模式實現要點:

1.遠端(Remote)代理:為一個位於不同位址空間的物件提供一個局域代表物件。這個不同的位址空間可以是在本機器中,也可是在另一台機器中。遠端代理又叫做大使(Ambassador)。好處是系統可以將網路的細節隱藏起來,使得用戶端不必考慮網路的存在。客戶完全可以認為被代理的物件是局域的而不是遠端的,而代理物件承擔了大部份的網路通訊工作。由於客戶可能沒有意識到會啟動一個耗費時間的遠端調用,因此客戶沒有必要的思想準備。
2.虛擬(Virtual)代理:根據需要創建一個資源消耗較大的物件,使得此物件只在需要時才會被真正創建。使用虛擬代理模式的好處就是代理物件可以在必要的時候才將被代理的物件載入;代理可以對載入的過程加以必要的優化。當一個模組的載入十分耗費資源的情況下,虛擬代理的好處就非常明顯。
3.Copy-on-Write代理:虛擬代理的一種。把Clone拖延到只有在用戶端需要時,才真正採取行動。
4.保護(Protect or Access)代理:控制對一個物件的訪問,如果需要,可以給不同的用戶提供不同級別的使用權限。保護代理的好處是它可以在執行時間對用戶的有關許可權進行檢查,然後在核實後決定將調用傳遞給被代理的物件。
5.Cache代理:為某一個目標操作的結果提供臨時的存儲空間,以便多個用戶端可以共用這些結果。
6.防火牆(Firewall)代理:保護目標,不讓惡意使用者接近。
7.同步化(Synchronization)代理:使幾個使用者能夠同時使用一個物件而沒有衝突。
8.智慧引用(Smart Reference)代理:當一個物件被引用時,提供一些額外的操作,比如將對此物件調用的次數記錄下來等。

-雲遊山水為知已逍遙一生而忘齡- 電腦神手