public delegate DoSomething();
public void Do()
{
Console.WriteLine(.......)
}
DoSomething=Do;
第一个属性属于(System.Reflection.MethodInfo)类型,MethodInfo描述了特定方法的签名,包括方法名称、参数和返回类型。除了MethodInfo,委托还需要一个对象实例,其中包含了要调用的方法。这正是第二个属性Target的作用。在静态方法的情况下,Target对应于类型自身。
实例方法和静态方法的Target属性:当委托对象指向一个实例方法时,委托对象不仅要维护这个方法的引用,而且还会维护到这个实例方法所属类的引用。System.Delegate的Target属性可以表示这个实例方法所属的类。
用委托写插入式的方法:一个委托变量会在运行时被指定一个方法。这个特性对于编写插件式的方法特别有用,本例中有一个名为Transform的公共方法,他对整型数组的每个元素进行变换,为了指定变换的方式,定义了一个名为TransFormer的委托参数:
public delegate int Transformer (int x);//定义的委托会在运行时与Util类中的Transform进行绑定
class Util
{
public static void Transform (int[] values, Transformer t)
{
for (int i = 0; i < values.Length; i++)
values[i] = t (values[i]);
}
}
class Test
{
static void Main()
{
int[] values = { 1, 2, 3 };
Util.Transform (values, Square); // Hook in the Square method
foreach (int i in values)
Console.Write (i + " "); // 1 4 9
}
static int Square (int x) { return x * x; }
}
SomeDelegate d = SomeMethod1;
d += SomeMethod2;
现在d不仅可以调用Some Method1,还可以调用SomeMethod2。委托按照添加的顺序依次被触发。运算符-和-=可以从左边的委托操作数中一出右边的委托操作数。
d-=SomeMethod1;
现在d只能触发SomeMethod2。
在值为null的delegate上面执行+=相当于为委托赋新值。
SomeDelegate d = null;
d += SomeMethod1;//当SomeDelegate为null时等价于d = SomeMethod1;
同样的,为只有一个目标方法的委托上面使用-或者-=相当于给委托赋予一个null值:
System.Threading.Thread.Sleep (100);
用来模拟运行很长时间的程序。public delegate void ProgressReporter (int percentComplete);
public class Util
{
public static void HardWork (ProgressReporter p)
{
for (int i = 0; i < 10; i++)
{
p (i * 10); // 调用委托
System.Threading.Thread.Sleep (100); // 模拟长时间的运行
}
}
}
为了监视执行进度,在Main方法中建立一个多播委托实例p,分别用两个方法来对程序的进度进行监视。
class Test
{
static void Main()
{
X x = new X();
ProgressReporter p = x.InstanceProgress;
p(99); // 99
Console.WriteLine (p.Target == x); // True
Console.WriteLine (p.Method); // Void InstanceProgress(Int32)
}
}
class X
{
public void InstanceProgress (int percentComplete)
{
Console.WriteLine (percentComplete);
}
}
public delegate T Transformer (T arg);
public class Util
{
public static void Transform (T[] values, Transformer t)
{
for (int i = 0; i < values.Length; i++)
values[i] = t (values[i]);
}
}
class Test
{
static void Main()
{
int[] values = { 1, 2, 3 };
Util.Transform (values, Square); // Hook in Square
foreach (int i in values)
Console.Write (i + " "); // 1 4 9
}
static int Square (int x) { return x * x; }
}
delegate TResult Func ();
delegate TResult Func <in T, out TResult> (T arg);
delegate TResult Func <in T1, in T2, out TResult> (T1 arg1, T2 arg2);
... and so on, up to T16
delegate void Action ();
delegate void Action <in T> (T arg);
delegate void Action <in T1, in T2> (T1 arg1, T2 arg2);
... and so on, up to T16
这些委托现在使用的非常普遍,前面举例的Transformer委托,可以用一个Func
public static void Transform (T[] values, Func transformer)
{
for (int i = 0; i < values.Length; i++)
values[i] = transformer (values[i]);
}
只有ref/out和指针没有在这些委托中涉及到。
在.net framework2.0之前,大多数代码使用的还是自定义的委托类型,因为那个时候还没有泛型,也不存在Func和Action。
- 委托和接口:能用委托解决的问题,都能用接口解决。例如:
public interface ITransformer
{
int Transform (int x);
}
public class Util
{
public static void TransformAll (int[] values, ITransformer t)
{
for (int i = 0; i < values.Length; i++)
values[i] = t.Transform (values[i]);
}
class Squarer : ITransformer
{
public int Transform (int x) { return x * x; }
}
...
static void Main()
{
int[] values = { 1, 2, 3 };
Util.TransformAll (values, new Squarer());
foreach (int i in values)
Console.WriteLine (i);
}
class Squarer : ITransformer
{
public int Transform (int x) { return x * x; }
}
class Cuber : ITransformer
{
public int Transform (int x) {return x * x * x; }
}
...
delegate void D1();
delegate void D2();
...
D1 d1 = Method1;
D2 d2 = d1; // Compile-time error
提示:但是允许下面的写法:
D2 d2 = new D2 (d1);
class Animal{}
class Bird : Animal{}
class Sparrow : Bird{}
static void Do(Bird shit){}
static void Main(string[] args)
{
Animal animal=new Animal();
Sparrow sparrow=new Sparrow();
Do(animal);//错误,无法从Animal转换为Bird:Bird=Animal这个向下转换是不安全的
Do(sparrow);//正确:Bird=Sparrow,向上转换是安全的
}
delegate void StringAction (string s);
class Test
{
static void Main()
{
StringAction sa = new StringAction (ActOnObject);
sa ("hello");
}
static void ActOnObject (object o)
{
Console.WriteLine (o); // hello
}
}
委托只是代替其他类来调用方法。在本例中,当调用StringAction时,使用的是string类型的参数,当这个string类型的参数被转发到目标方法时(在本例中一个object类型参数的方法),string将被向上转换为object(隐式)。
class Animal{}
class Bird : Animal{}
class Sparrow : Bird{}
static Bird Do()
{
return new Bird();
}
Animal animal=new Animal();
Sparrow sparrow=new Sparrow();
animal = Do();
sparrow = Do(); //转换失败,Bird无法隐式转换成Sparrow
在这里可以总结下:在输入位置提供一个子类或者在输出位置提供一个基类,都是安全的,都是多态的表现,反之则不然。
delegate object ObjectRetriever();
class Test
{
static void Main()
{
ObjectRetriever o = new ObjectRetriever (RetriveString);
object result = o();
Console.WriteLine (result); // hello
}
static string RetriveString() { return "hello"; }
}
ObjectRetriever期望返回一个object,而它指向的方法返回一个string。
delegate TResult Func<out TResult>();
允许:
Func x = ...;
Func
下面的委托支持逆变:
delegate void Action<in T> (T arg);
允许:
Action
// Delegate definition
public delegate void PriceChangedHandler (decimal oldPrice,decimal newPrice);
public class Broadcaster
{
// Event declaration
public event PriceChangedHandler PriceChanged;
}
Broadcaster类中的代码对PriceChanged有完全的访问权限,并把它当作一个委托,Broadcaster类外的代码只能使用+=和-=在PriceChanged事件上。