Effective C#之24:Prefer Declarative to Imperative Programming

来源:互联网 发布:软件快捷方式打不开 编辑:程序博客网 时间:2024/05/21 05:22
 

Item 24: Prefer Declarative toImperative Programming

优先选择声明式编程而不是命令式编程

Declarative programming can oftenbe a simpler, more concise way to describe the behavior of a software programthan imperative programming. Declarative programming means that you define thebehavior of your program using declarations instead of by writing instructions.In C#, as in many other languages, most of your programming is imperative: Youwrite methods that define the behavior of your programs. You practicedeclarative programming using attributes in C#. You attach attributes toclasses, properties, data members, or methods, and the .NET runtime addsbehavior for you. This declarative approach is simpler to implement and easierto read and maintain.

与命令式编程相比,声明式编程是描述软件程序行为的更简单,更精准的方式。声明式编程意味着通过声明而不是编写命令来定义程序的行为。在C#里面,和在其他语言里面一样,你的多数编程是命令式的:编写定义程序行为的方法。在C#里面通过使用特性来实现声明式编程。可以将特性绑定到类、属性、数据成员或者方法上,.Net的运行时会为你添加行为。这种声明式的尝试易实现、易读、易维护。

Let's begin with an obviousexample that you've already used. When you wrote your first ASP.NET webservice, the wizard generated this sequence of code:

以一个你已经使用过的明显的例子来开始吧。当你编写第一个ASP.NET页面服务的时候,想到会生成这样的代码。

    [WebMethod]

    publicstring HelloWorld()

    {

      return"Hello World";

}

The VS .NET Web Service wizardadded the [WebMethod] attribute to the HelloWorld() method. That declaredHelloWorld as a web method. The ASP.NET runtime creates code for you inresponse to the presence of this attribute. The runtime created the Web ServiceDescription Language (WSDL) document, which contains a description for the SOAPdocument that invokes the HelloWorld method. ASP.NET also adds support in theruntime to route SOAP requests to your HelloWorld method. In addition, theASP.NET runtime dynamically creates HTML pages that enable you to test your newweb service in IE. That's all in response to the presence of the WebMethodattribute. The attribute declared your intent, and the runtime ensured that theproper support was there. Using the attribute takes much less time and is muchless error prone.

VS.NET页面服务向导为HelloWorld()方法添加了[WebMethod]特性,这将HelloWorld声明为一个页面方法。作为对这个特性存在的响应,ASP.NET运行时为你创建代码。运行时创建一个页面服务描述语言(WSDL)文档,其中包含了对调用HelloWorld方法的SOAP文档的描述。ASP.NET在运行时也为你的HelloWorld方法添加对SOAP请求的路由支持。另外,ASP.NET运行时动态创建HTML页面,使得你在IE里面测试你的新的web服务。这些都是对WebMethod特性做出的响应。该特性声明了你的意图,运行时给你提供了恰当的保障。使用特性可以花费更少的时间,并且更少出错。

It's really not magic. The ASP.NETruntime uses reflection to determine which methods in your class are webmethods. When they are found, the ASP.NET runtime can add all the necessaryframework code to turn any function into a web method.

这并不神奇。ASP.NET运行时使用反射来检测你的类里面的那个方法是web方法。当它们被找到时,ASP.NET会加入所有必须的框架代码将任何方法转变为一个web方法。

The [WebMethod] attribute is justone of many attributes that the .NET library defines that can help you createcorrect programs more quickly. A number of attributes help you createserializable types (see Item 25). As you saw in Item 4, attributes controlconditional compilation. In those and other cases, you can create the code youneed faster and with less chance for errors using declarative programming. Youshould use these .NET Framework attributes to declare your intent rather thanwrite your own code. It takes less time, it's easier, and the compiler doesn'tmake mistakes.

[WebMethod]特性只是.NET库里面定义的众多特性中的一个,这些特性可以帮助你更快速的创建正确的程序。一些特性帮助你创建系泪花类型(Item25)。正如你在Item4里面看到的一样,特性控制着条件编译。在那些或者其他情况下,使用声明式编程,你可以更快的并且少出错的创建需要的代码。你应该使用这些.Net框架特性来声明你的意图,而不是自己编写代码。这会花费更少的时间,也更容易,同时编译器也不会出错。

If the predefined attributes don'tfit your needs, you can create your own declarative programming constructs bydefining custom attributes and using reflection. As an example, you can createan attribute and associated code to let users create types that define thedefault sort order using an attribute. A sample usage shows how adding theattribute defines how you want to sort a collection of customers:

如果预定义的特性不能满足你的需求,通过定义自己的特性然后使用反射,你可以创建自己的声明式编程结构。例如,你可以创建特性和相关的代码,让用户创建使用特性来定义默认排序的类型。下面的代码示例演示了如何通过添加特性来定义你希望如何对Customer集合进行排序。

    [DefaultSort("Name")]

    publicclass Customer

    {

        publicstring Name

        {

            get{ return name; }

            set{ name = value; }

        }

 

        publicdecimal CurrentBalance

        {

            get{ return balance; }

        }

 

        publicdecimal AccountValue

        {

            get

            {

                return calculateValueOfAccount();

            }

        }

 }

The DefaultSort attribute e, theName property. The implication is that any collection of Customers should beordered by the customer name. The DefaultSort attribute is not part of the .NETFramework. To implement it, you need to create the DefaultSortAttribute class:

DefaultSort 特性为Customer类定义了默认的排序属性:Name。隐含的意思是,任何Customer的集合应该按照Customername来排序。DefaultSort不是.NET框架的一部份。为了实现它,你需要创建DefaultSortAttribute类。

    [AttributeUsage(AttributeTargets.Class |AttributeTargets.Struct)]

    publicclass DefaultSortAttribute: System.Attribute

    {

        privatestring name;

        publicstring Name

        {

            get{ return name; }

            set{ name = value; }

        }

 

        publicDefaultSortAttribute(string name)

        {

            this.name= name;

        }

 }

You must still write the code tosort a collection of objects based on the presence of the DefaultSortattribute. You'll use reflection to find the correct property and then comparevalues of that property in two different objects. The good news is that youneed to write this code only once.

由于DefaultSort特性的出现,你还应该编写代码来对一个对象的集合进行排序。你将使用反射来找到正确的属性,然后对2个不同对象里面的那个属性值进行比较。好消息就是你仅仅需要编写一次这个代码。

Next, you create a class thatimplements IComparer. (Comparers are discussed in more detail in Item 26.)IComparer has a version of CompareTo() that compares two objects of a giventype, letting the target class, which implements IComparable, define the sortorder. The constructor for the generic comparer finds the default sort propertydescriptor based on the type being compared. The Compare method sorts twoobjects of any type, using the default sort property:

接下来,你创建一个实现IComparer的类(在Item26里面将详细讨论Comparer)。IComparer有一个版本的CompareTo()来比较一个给定类型的2个对象,允许实现了IComparable的目标类型,定义排序顺序。GenericComparer构造器会基于正在被比较的类型,来寻找默认的排序属性描述。Comparer方法使用默认的排序属性为任何类型的2个对象进行排序。

    internalclass GenericComparer: IComparer

    {

        // Information aboutthe default property:

        privatereadonly PropertyDescriptor sortProp;

 

        //Ascending or descending.

        privatereadonly boolreverse = false;

 

        //Construct for a type

        publicGenericComparer(Type t):this(t, false)

        {

        }

 

        //Construct for a type

        //and a direction

        publicGenericComparer(Type t, bool reverse)

        {

            reverse = reverse;

            //find the attribute,

            //and the name of the sort property:

 

            //Get the default sort attributes on the type:

            object[]a = t.GetCustomAttributes(typeof(DefaultSortAttribute),false);

 

            //Get the PropertyDescriptor for that property:

            if(a.Length > 0)

            {

                DefaultSortAttribute sortName = a[0] as DefaultSortAttribute;

                string name = sortName.Name;

 

                // Initialize the sort property:

               PropertyDescriptorCollection props = TypeDescriptor.GetProperties(t);

                if (props.Count > 0)

                {

                    foreach (PropertyDescriptor p in props)

                    {

                        if (p.Name == name)

                        {

                            // Found the default sort property:

                            sortProp= p;

                            break;

                        }

                    }

                }

            }

        }

 

        //Compare method.

        intIComparer.Compare(object left,object right)

        {

            //null is less than any real object:

            if((left == null) && (right == null))

                return 0;

            if(left == null)

                return -1;

            if(right == null)

                return1;

 

            if(sortProp == null)

                return 0;

 

            //Get the sort property from each object:

            IComparablelField = sortProp.GetValue(left) as IComparable;

            IComparablerField = sortProp.GetValue(right) as IComparable;

            intrVal = 0;

            if(lField == null)

                if (rField == null)

                    return 0;

                else

                    return -1;

            rVal =lField.CompareTo(rField);

            return(reverse) ? -rVal : rVal;

        }

}

The Generic comparer sorts anycollection of Customers based on the property declared in the DefaultSortattribute:

GenericComparer对任何Customer的集合按照DefaultSort里面声明的属性进行排序。

CustomerList.Sort( new GenericComparer(typeof(Customer )));

The code to implement theGenericComparer makes use of advanced techniques, such as reflection (see Item43). But you need to write it only once. From that point on, all you need to dois add the attribute to any class, and you can sort a collection of thoseobjects using the generic comparer. If you change the parameter on theDefaultSort attribute, you change the class's behavior. You don't need to changeany algorithms anywhere in your code.

实现GenericComparer的代码利用了像反射(Item43)这样的高级技术,但是你仅仅需要编写一次。从那之后,你所需要做的就是将这个属性添加到任何类上面,然后你就可以通过GenericComparer来对那些对象的集合进行排序了。如果你改变了DefaultSort特性的参数,你就改变了类的行为。你不需要在你的代码的任何地方修改任何算法。

This declarative idiom is usefulto avoid writing repetitive code when a simple declaration can specify yourintent. Look at the Generic Comparer class again. You could write different(and slightly simpler) versions of the sort algorithm for every type youcreated. The advantage to using declarative programming is that you can writeone generic class and let a simple declaration create the behavior for eachtype. The key is that the behavior changes based on a single declaration, notbased on any algorithm changes. The GenericComparer works for any typedecorated with the DefaultSort attribute. If you need sorting functionalityonly once or twice in your application, write the simpler routines. However, ifyou might need the same behavior for many tens of different types in yourprogram, the generic algorithm and the declarative solution will save you timeand energy in the long run. You'd never write all the code generated by theWebMethod attribute. You should expand on that technique for your ownalgorithms. Item 42 discusses one example: how to use attributes to buildadd-on command handlers. Other examples might include anything from definingadd-on packages to building dynamic web page UIs.

当一个简单的声明能表述你的意图时,这种声明式的习惯在避免编写重复代码上很有用。再次看看Generic Comparer类。你可以为每个自己创建的类型编写不同版本(也更简单)的排序算法。使用声明式编程的优点是你可以编写一个泛型类,让一个简单的声明为每个类型创建行为。关键是,行为基于一个单独的声明而改变,而不是基于算法的改变。GenericComparer可以在任何使用了DefaultSort特性的类型下工作。如果在你的应用程序里面,只需要一两次排序功能,就编写简单的程序代码好了。然而,如果在你的程序里,你可能在成千上万的类型上面都需要同样的行为,那么,泛型算法和声明式解决方法将会节省你的时间和精力。你不需要编写由WebMethod特性生成的所有代码。你应该为你自己的算法扩展该技术。Item 42讨论了一个例子:如何使用特性来构建附加的命令处理者。其他的例子可能包含了从定义扩展包到构建动态web页面UI的任何东西。

Declarative programming is apowerful tool. When you can use attributes to declare your intent, you save thepossibility of logic mistakes in multiple similar hand-coded algorithms.Declarative programming creates more readable, cleaner code. That means fewermistakes now and in the future. If you can use an attribute defined in the .NETFramework, do so. If not, consider the option of creating your own attributedefinition so that you can use it to create the same behavior in the future.

声明式编程是一个强大的工具。当你可以使用特性来声明你的意图时,避免了在多个类似的手工编写的算法中,犯逻辑错误的可能。声明式编程创建更可读的、更清晰的代码。在现在和以后那都意味着更少的错误。如果你可以使用在.Net框架里面定义的特性,就用吧。如果不能,考虑创建自己的特性类,那样的话,在以后就可以使用它来创建同样的行为。

原创粉丝点击