架构深渊

慢慢走进程序的深渊……关注领域驱动设计、测试驱动开发、设计模式、企业应用架构模式……积累技术细节,以设计架构为宗。
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

在Visual C# 2.0中创建优雅代码6

Posted on 2009-01-02 01:59  chen eric  阅读(223)  评论(0编辑  收藏  举报
委托推理
    C#编译器从匿名方法指派推理哪个委托类型将要实例化的能力是一个非常重要的功能。实际上,它还提供了另一个叫做委托推理的C# 
2.0功能。委托推理允许直接给委托变量指派方法名,而不需要先使用委托对象包装它。例如下面的C# 1.1代码:

class SomeClass
{
    
delegate void SomeDelegate();

    
public void InvokeMethod()
    {
        SomeDelegate del 
= new SomeDelegate(SomeMethod);
        del();
    }

    
void SomeMethod()
    {}
}

    现在,可以编写下面的代码来代替前面的代码片断:

class SomeClass
{
    
delegate void SomeDelegate();

    
public void InvokeMethod()
    {
        SomeDelegate del 
= SomeMethod;
        del();
    }

    
void SomeMethod()
    {}
}

    当将一个方法名指派给委托时,编译器首先推理该委托的类型。然后,编译器根据此名称检验是否存在一个方法,并且它的调用是否与推理的委托类型相匹配。最后,编译器创建一个推理委托类型的新对象,以便包装此方法,并将其指派给该委托。如果该类型是一个具体的委托类型(即除了抽象类型Delegate之外的其他类型),则编译器只能推理委托类型。委托推理的确是一个非常有用的功能,它可以使代码变得简练而优雅。

    作为C# 
2.0中的惯例,开发人员将会使用委托推理,而不是以前的委托实例化方法。例如,下面的代码说明了如何在不显式地创建一个ThreadStart委托的情况下启动一个新的线程:

public class MyClass

    
void ThreadMethod() 
    {}

    
public void LauchThread()
    {
        Thread workerThread 
= new Thread(ThreadMethod); 
        workerThread.Start();
    }
}

    当启动一个异步调用并提供一个完整的回调方法时,可以使用一对委托推理,如图11所示。首先,指定异步调用的方法名来异步调用一个匹配的委托。然后调用BeginInvoke,提供完整的回调方法名而不是AsyncCallback类型的委托。

class SomeClass

    
delegate void SomeDelegate(string str); 

    
public void InvokeMethodAsync() 
    { 
        SomeDelegate del 
= SomeMethod; 
        del.BeginInvoke(
"Hello",OnAsyncCallBack,null);
    } 

    
void SomeMethod(string str)
    {
        MessageBox.Show(str);
    }

    
void OnAsyncCallBack(IAsyncResult asyncResult) 
    {}
}

图 
11 使用委托推理

 属性和索引可见性
    C# 
2.0允许为属性或索引器的get和set访问器指定不同的可见性。例如,在通常情况下,可能想将get访问器公开为public,而把set访问器公开为protected。为此,可以为set关键字添加protected可见性限定符。类似地,可以将索引器的set方法定义为protected(请参见图12)。

public class MyClass
{
    
public string this[int index]
    {
        
get
        {
            
return m_Names[index];
        }
        
protected set
        {
            m_Names[index] 
= value;
        }
    }

    
string[] m_Names; 
    
//Rest of the class
}

图 
12 Public Get 与 Protected Set

    当使用属性可见性时有几项规定。首先,应用在set或get上的可见性限定词只能是此属性本身可见性的严格子集。换句话说,如果此属性是public,那么就可以指定internal、
protectedprotected internalprivate。如果此属性可见性是protected,就不能将get或set公开为public。此外,只能分别为get或set指定可见性,而不能同时为它们指定可见性。静态类有些类只有静态方法或静态成员(静态类),这是非常常见的。在这种情况下,实例化这些类的对象没有意义。例如,Monitor类或类工厂(例如.NET框架1.1中的Activator类)都是静态类。在C# 1.1中,如果想要阻止开发人员实例化类的对象,可以只提供一个私有的默认构造函数。如果没有任何公共的构造函数,就不可以实例化类的对象:

public class MyClassFactory 
{
    
private MyClassFactory() 
    {}

    
static public object CreateObject()
    {}
}

    然而,因为C#编译器仍然允许添加实例成员(尽管可能从来都不使用它们),所以是否在类中只定义静态成员完全由您决定。C# 
2.0通过允许将类限定为static来支持静态类:

public static class MyClassFactory

    
static public T CreateObject()
    {}
}

    C# 
2.0编译器不允许将一个非静态成员添加到一个静态类中,也不允许创建此静态类的实例,就好像它是一个抽象类一样。此外,不能从一个静态类派生子类。这就如同编译器在静态类定义中加入了abstract和sealed一样。注意,可以定义静态类而不能定义静态结构,并且可以添加静态构造函数。

 全局命名空间限定符
    很可能有这样一个嵌套的命名空间,它的名称与一些其他的全局命名空间相匹配。在这种情况下,C# 
1.1编译器在解析命名空间引用时会出现问题。请考虑下例:

namespace MyApp
{
    
namespace System
    {
        
class MyClass
        {
            
public void MyMethod()
            {
                System.Diagnostics.Trace.WriteLine(
"It Works!");
            }
        }
    }
}

    在C# 
1.1中,调用Trace类会产生编译错误(没有全局命名空间限定符::)。出现这种错误的原因在于,当编译器尝试解析对System命名空间的引用时,它使用直接包含范围,此范围包含System命名空间但不包含Diagnostics命名空间。C# 2.0允许您使用全局命名空间限定符::来表示编译器应该在全局范围内进行搜索。可以将::限定符应用于命名空间和类型,如图13所示。

namespace MyApp
{
    
class MyClass
    {
        
public void MyMethod()
        {
            ::MyClass obj 
= new ::MyClass();
            obj.MyMethod(); 
// Traces "Hello" instead of recursion 
        }
    }
}

public class MyClass
{
    
public void MyMethod()
    {
        Trace.WriteLine(
"Hello"); 
    }
}

图 
13 使用全局命名空间限定符

 内联警告
    C# 
1.1允许使用项目设置或者通过向编译器发布命令行参数来禁止特殊的编译器警告。但是这是一个全局性的取消,这样做会取消一些仍然需要的警告。C# 2.0允许使用#pragma警告指令显式地取消和恢复编译器警告:

// Disable 'field never used' warning
#pragma warning disable 169
public class MyClass 
{
    
int m_Number;
}
#pragma warning restore 169

    在产品代码中通常并不鼓励禁止警告。禁止警告只是为了进行某些分析,比如,当您尝试隔离一个问题时,或者当您设计代码并且想要得到代码合适的初始结构而不必先行对其加以完善时。而在所有其他的情况下,都要避免取消编译器警告。注意,不能通过编程的方式来重写项目设置,这意味着不能使用pragma警告指令来恢复全局取消的警告。

 小结
    本文所提到的C# 
2.0中的一些新功能是专门的解决方案,旨在处理特定的问题,同时可以简化整体编程模型。如果关注工作效率和质量,就需要让编译器生成尽可能多的实现,减少重复性的编程任务,使最后得到的代码简洁易读。新的功能带来的正是这些特色,它们象征着C#时代的到来,将会使C#成为服务于.NET专业开发人员的优秀工具