1. 程式人生 > >精通C#--高階C#語言特性

精通C#--高階C#語言特性

高階C#語言特性

1.索引器
1.1.索引器方法

public class PersonCollection : IEnumerable
{
    private ArrayList arPeople = new ArrayList();
    // 類的自定義索引器
    public Person this[int index]
    {
        get{return (People)arPeople[index];}
        set{arPeople.Insert(index, value);};
    }
    ...
}

1.2.使用字串值索引物件

public class PersonCollection : IEnumerable
{
    private Dictionary<string, Person> listPeople = new Dictionary<string, Person>();
    public Person this[string name]
    {
        get{ return listPeople[name];}
        set{ listPeople[name] = value;}
    }

    public void ClearPeople
() { listPeople.Clear(); } public int Count { get{ return listPeople.Count;} } IEnumerator IEnumerable.GetEnumerator() { return listPeople.GetEnumerator(); } } static void Main() { PersonCollectioni myPeople = new PersonCollection(); myPeople["Homer"
] = new Person("Homer", "Simpson", 40); myPeople["Marge"] = new Person("Marge", "Simpson", 38); Person homer = myPeople["Homer"]; Console.WriteLine(homer.ToString()); Console.ReadLine(); }

1.3.過載索引器方法

public sealed class DataTableCollection : InternalDataCollectionBase
{
    // 過載的索引器
    public DataTable this[string name]{get;}
    public DataTable this[string name, string tableNamespace]{get;}
    public DataTable this[int index]{get;}
}

1.4.多維的索引器

public class SomeContainer
{
    private int[,] my2DintArray = new int[10, 10];
    public int this[int row, int column]
    {}
}

1.5.在介面型別上定義索引器

public interface IStringContainer
{
    string this[int index]{get; set;}
}

class SomeClass : IStringContainer
{
    private List<string> myStrings = new List<string>();
    public string this[int index]
    {
        get{return myStrings[index];}
        set{myStrings.Insert(index, value);}
    }
}

操作符過載

C#操作符可過載性

+, -, !, ~, ++, --, true, false 可過載
+, -, *, /, %, &, |, ^, <<, >> 可過載
==, !=, <, >, <=, >= 可過載【<><=和>=, == 和 != 過載時要一起】
[]  索引器提供了類似的功能,故,不再支援過載
()  自定義轉換方法提供了同樣的功能,故,不再支援過載
+=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>= 不可過載,但相關二元操作符過載時,也隨之具備新功能

operator 允許自定義型別對內建操作符做出不同反應,且只可與static關鍵字聯合使用。

public class Point : IComparable<Point>
{
    ...
    public static Point operator+(Point p1, Point p2)
    {
        return new Point(p1.X + p2.X, p1.Y + p2.Y);
    }

    public static Point operator-(Point p1, Point p2)
    {
        return new Point(p1.X-p2.X, p1.Y-p2.Y);
    }

    // C#中不能獨立過載前後遞增/遞減。
    public static Point operator++(Point p1)
    {
        return Point(p1.X+1, p1.Y+1);
    }
    public static Point operator--(Point p1)
    {
        return new Point(p1.X-1, p1.Y-1);
    }

    // System.Object.Equals()可以重寫,實現引用型別間基於值的比較
    // 如果選擇重寫Equals和與之密切相關的System.Object.GetHashCode,則,再過載==和!=意義不大
    public override bool Equals(object o)
    {
        if(o == null) return false;
        return o.ToString() == this.ToString();
    }

    public override int GetHashCode()
    {
        return this.ToString().GetHashCode();
    }

    public static bool operator==(Point p1, Point p2)
    {
        return p1.Equals(p2);
    }

    public static bool operator!=(Point p1, Point p2)
    {
        return !p1.Equals(p2);
    }

    // 過載比較操作符
    public int CompareTo(Point other)
    {
        if(this.X > other.X && this.Y > other.Y)
        {
            return 1;
        }

        if(this.X < other.X && this.Y < oher.Y)
        {
            return -1;
        }
        else
        {
            return 0;
        }
    }

    public static bool operator<(Point p1, Point p2)
    {
        return (p1.CompareTo(p2) < 0);
    }

    public static bool operator>(Point p1, Point p2)
    {
        return (p1.CompareTo(p2) > 0);
    }

    public static bool operator<=(Point p1, Point p2)
    {
        return (p1.CompareTo(p2) <= 0);
    }

    public static bool operator>=(Point p1, Point p2)
    {
        return (p1.CompareTo(p2) >= 0);
    }
}

// 
Point p1 = new Point(10, 10);
Point p2 = new Point(10, 10);
Point p3 = p1 + p2;
Point p4 = Point.operator+(p1, p2);

// 因為過載了+和-,故+=, -=自動具備過載後的表現

自定義型別轉換

1.
C#允許我們構建能使使用者型別響應()操作符的自定義強制型別轉換例程。

2.

public struct Rectangle
{
    public int Width{get; set;}
    public int Height{get; set;}
    public Rectangle(int w, int h) : this()
    {
        Width = w;
        Height = h;
    }

    public void Draw()
    {
        for(int i = 0; i < Height; i++)
        {
            for(int j = 0; j < Width; j++)
            {
                Console.Write("*");
            }

            Console.WriteLine();
        }
    }

    public override string ToString()
    {
        return string.Format("[Width={0}; Height={1}]", Width, Height);
    }
}

public struct Square
{
    public int Length{get; set;}
    public Square(int l) : this()
    {
        Length = l;
    }

    public void Draw()
    {
        for(int i = 0; i < Length; i++)
        {
            for(int j =0; j < Length; j++)
            {
                Console.Write("*");
            }

            Console.WriteLine();
        }
    }

    public override string ToString()
    {
        return string.Format("[Length={0}]", Length);
    }

    public static explicit operator Square(Rectangle r)
    {
        Square s = new Square();
        s.Length = r.Height;
        return s;
    }

    public static explicit operator Square(int sideLength)
    {
        Square newSq = new Square();
        newSq.Length = sideLength;
        return newSq;
    }

    public static explicit operator int(Square s)
    {
        return s.Length;
    }

    public static implicit operator Rectangle(Square s)
    {
        Reactangle r = new Rectangle();
        r.Height = s.Length;
        r.Width = s.Length * 2;
        return r;
    }
}

3.
轉換例程使用C# operator,結合使用explicit或implicit 且 必須定義為靜態的。
傳入引數是要轉換的實體,操作符型別是轉換後的實體。

擴充套件方法

允許你在不直接修改原始型別下,為類或結構新增新的方法或屬性。

1.定義擴充套件方法
必須把方法定義在靜態類中。故,每一個擴充套件方法須宣告為靜態的。
所有擴充套件方法都要使用this對第一個引數【且僅對第一個引數】進行修飾。

2.

static class MyExtensions
{
    // 擴充套件方法的第一個引數表示被拓展的型別。需要用this修飾。
    // 後續其它引數,即為方法的普通傳入引數
    public static void DisplayDefiningAssembly(this object obj)
    {
        Console.WriteLine("{0} lives here:=>{1}\n", obj.GetType().Name, Assembly.GetAssembly(obj.GetType()).GetName().Name);
    }

    public static int ReverseDigits(this int i)
    {
        char[] digits = i.ToString().ToCharArray();
        Array.Reverse(digits);
        string newDigits = new string(digits);
        return int.Parse(newDigits);
    }
}

3.匯入擴充套件方法
在定義包含擴充套件方法的類時,應將其定義在.NET名稱空間中。
如果該命名與使用擴充套件方法的名稱空間不同,就需在使用擴充套件方法的哪裡使用using關鍵字。

4.擴充套件實現了指定介面的型別

static class AnnoyingExtensions
{
    public static void PrintDataAndBeep(this System.Collections.IEnumerable iterator)
    {
        foreach(var item in iterator)
        {
            Console.WriteLine(item);
            Console.Beep();
        }
    }
}

static void Main()
{
    Console.WriteLine("***xxx***\n");
    string[] data = {"1", "2", "3"};
    data.PrintDataAndBeep();
    Console.WriteLine();
    List<int> myInts = new List<int>(){10, 15, 20};
    myInts.PrintDataAndBeep();
    Console.ReadLine();
}

匿名型別

1.定義匿名型別
當定義一個匿名型別時,需使用關鍵字var和物件初始化語法。

static void BuildAnonType(string make, string color, int currSp)
{
    var car = new {Make = make, Color = color, Speed = currSp};
    Console.WriteLine("You have a {0} {1} going {2} MPH", car.Color, car.Make, car.Speed);
    Console.WriteLine("ToString() == {0}", car.ToString());
}

2.
所有的匿名型別都自動繼承System.Object,
匿名型別的型別名完全由編譯器決定。

3.建立由匿名型別組成的匿名型別

var purchaseItem = new 
{
    TimeBought = DateTime.Now,
    ItemBought = new {Color = "Red", Make = "Saab", CurrentSpeed = 55},
    Price = 34.000
};

指標型別

1.編譯時,須指定 /unsafe
2.進行指標操作的區域用 unsafe{} 包圍。

class Program
{
    static void Main(string[] args)
    {
        unsafe
        {
            // 可直接使用指標區域
        }
    }
}

// 不安全的結構,僅可用於unsafe上下文中
unsafe struct Node
{
    public int Value;
    public Node* Left;
    public Node* Right;
}

// 這個結構可以用在非unsafe上下文,但結構Node2*成員只能用在unsafe上下文。
public struct Node2
{
    public int Value;
    // 只能在unsafe上下文訪問
    public unsafe Node2* Left;
    public unsafe Node2* Right;
}

// 此函式只有在不安全上下文才能被呼叫
unsafe static void SquareIntPointer(int* myIntPointer)
{
    *myIntPointer *= *myIntPointer;
}

unsafe
{
    int *p1, *p2;// X
    int* p1, p2;// C#中這裡和C/C++不同。
}

在不安全上下文中,可能需要宣告一個直接從呼叫棧分配記憶體的本地變數。

unsafe static void UnsafeStackAlloc()
{
    char* p = stackalloc char[256];
    for(int k = 0; k < 256; k++)
        p[k] = (char)k;
}

使用fixed關鍵字

class PointRef
{
    public int x;
    public int y;
    public override string ToString()
    {
        return string.Format("({0}, {1})", x, y);
    }
}

class MyTest
{
    unsafe public static void UseAndPinPoint()
    {
        PointRef pt = new PointRef();
        pt.x = 5;
        pt.y = 6;

        // 在適當位置固定pt以免GC除去
        fixed(int* p = &pt.x)
        {
            // 在此使用int*變數
        }
    }
}

任何時候在不安全程式碼上下文中與引用型別互動,都要固定該引用。

sizeof在不安全程式碼中用於獲取型別尺寸大小。