泛型約束

泛型約束是使用where關鍵字讓泛型的類型有一定的限制

  • where T:struct :結構類型的約束,只能接收結構類型作為泛型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class Test1<T> where T:struct
    {
    public T value;
    public void TestFun<K>(K v) where K: struct
    }

    // 這句會報錯,因為他不是結構類型
    //-- Test1<object> t = new Test1<object>();
    // 這句可以
    Test1<int> t2 = new Test1<int>();
  • where T:class :引用類型約束

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Test2<T> where T : class
    {
    public T value;
    public void TestFun<K>(K k) where K : class {}
    }

    // 這句可以
    Test2<object> t = new Test2<object>();

    // 這句會報錯,因為 int 不是引用類型
    //-- Test2<int> t2 = new Test2<int>();
  • where T: new() :這個泛型一定要有一個無參數public的建構子(constructor),此外若是組合使用的話new()要放到最後

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class Test3<T> where T : new()
    {
    public T value;
    }

    class PubC1 {}
    class PubC2
    {
    public PubC2(int i) {}
    }

    // 這句可以
    Test3<PubC1> t = new Test3<PubC1>();

    // 這句不可以,因為 PubC2 沒有無參數的 public 建構子
    //-- Test3<PubC2> t2 = new Test3<PubC2>();
  • where T: 類名 :泛型參數必須要是其類或者是其子類

  • where T: 介面名 :泛型參數必須要是其介面的衍伸類型

  • where T:U:泛型參數為另一個泛型本身或是其衍伸類型

    1
    2
    3
    class Test6<T,U> where T:U{
    public T value;
    }
  • 多個泛型皆有約束

    1
    class Test8<K,V> where K:class,new() where K:struct{}

委派 Delegate

委派:delegate

  • 它的本質上是一個Class,主要用來定義method的類型
  • 可以把它想像為一個定義method容器,用來儲存要傳遞的method。
  • 使用委派讓你可以在呼叫method先處理一些邏輯,當這些邏輯處理完之後,在傳入這些method。

以下是他的語法

1
存取修飾詞 delegate 返回值 委派名(參數列表);
  • 如果沒寫存取修飾詞的話,預設為public的,
  • 若取修飾詞寫為private的話,則其他namespace不可以使用
  • 委派的method參數必須要對應

宣告範例

1
2
3
4
5
// 定義了一個無參無返回值容器 MyFun
delegate void MyFun();

// 定義了一個返回值為int,有一個int參數的委派容器
delegate int MyFun2(int a);

使用範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
delegate void MyFun();
delegate int MyFun2(int a);
class Program
{
static void Main()
{
Console.WriteLine("委派");

// 裝載method的容器
MyFun f = new MyFun(Fun);
// 呼叫容器裡面的method
f.Invoke();

// 簡化寫法,與f的方式一樣
MyFun f2 = Fun;
f2();

// 容器與委派的method參數必須要對應
MyFunc2 f3 = Fun2;
f3(1);
}

static void Fun()
{
Console.WriteLine("123456");
}

static int Fun2(int value)
{
Console.WriteLine(value);
return value;
}
}

C#預設先定義的委派

  • Action:是一個無參無返回值的委派
  • Func:一個無參數,但有一個返回泛型TResult的委派
  • Action(T obj)可以傳多個參數(最多有16個,是使用overload的方式),無返回的委派
  • Func<in T, out TResult>(T arg); 可以傳多個參數,且有一個返回泛型TResult的委派
  • 記憶方式:Func是有返回值的委託,而Action則是不會返回值的委託

Action例子

1
2
3
4
5
6
7
Action<int, string> action = FunTest;

static void FunTest(int a, string b)
{
// do something...
}

Func例子

1
2
3
4
5
6
7
Func<int, int> func = FunTest2;

static int FunTest2(int value)
{
Console.WriteLine(value);
return value;
}

C# 明確方式實作介面

明確方式實作介面(Explicit Interface Implementation)

如果一個class去實作兩個有相同方法的interface,會發生什麼事?
程式如下,分別有兩個interface IControlIAction都含有Move()方法,其中ExampleClass去實作這兩個Interface。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface IControl
{
void Move();
}
public interface IAction
{
void Move();
}
public class ExampleClass : IControl, IAction
{
// Both IAction.Move and IControl.Move call this method.
public void Move()
{
Console.WriteLine("Move method in ExampleClass");
}
}

執行

1
2
3
4
5
6
7
8
9
10
11
12
13
ExampleClass example = new ExampleClass();
IControl control = example;
IAction action = example;

// The following lines all call the same method.
example.Move();
control.Move();
action.Move();

// Output:
// Move method in ExampleClass
// Move method in ExampleClass
// Move method in ExampleClass

結果是都會呼叫同一個實作。


但是你可能希望個自Interface執行不同的實作,這時就可以使用Explicit Interface Implementation如下:

1
2
3
4
5
6
7
8
9
10
11
public class ExampleClass2 : IControl, IAction
{
void IControl.Move()
{
System.Console.WriteLine("IControl.Move");
}
void IAction.Move()
{
System.Console.WriteLine("IAction.Move");
}
}

執行

1
2
3
4
5
6
7
8
9
10
11
12
ExampleClass2 example2 = new ExampleClass2();
IControl control = example2;
IAction action = example2;

// The following lines all call the same method.
//example2.Move(); // Compiler error.
control.Move(); // Calls IControl.Move on ExampleClass2.
action.Move(); // Calls IAction.Move on ExampleClass2.

// Output:
// IControl.Move
// ISurface.Move

此時,不可以直接透過實作class的物件去直接呼叫,會出現編譯錯誤,必須要把該物件轉換為對應的interface,例如如果你想呼叫IControl的Move方法,那麼就把它轉換為IControl。

注意:Explicit Interface Implementation是沒有存取修飾詞的,因為它無法當做其定義類型的成員來存取。它只有在透過interface的執行個體呼叫時才能存取。

參考:https://learn.microsoft.com/zh-tw/dotnet/csharp/programming-guide/interfaces/explicit-interface-implementation