Effective C#之24:Prefer Declarative to Imperative Programming

  rel="File-List" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_filelist.xml"> rel="themeData" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_themedata.thmx"> rel="colorSchemeMapping" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_colorschememapping.xml">

Item 24: Prefer Declarative to Imperative Programming

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

Declarative programming can often be a simpler, more concise way to describe the behavior of a software program than imperative programming. Declarative programming means that you define the behavior of your program using declarations instead of by writing instructions. In C#, as in many other languages, most of your programming is imperative: You write methods that define the behavior of your programs. You practice declarative programming using attributes in C#. You attach attributes to classes, properties, data members, or methods, and the .NET runtime adds behavior for you. This declarative approach is simpler to implement and easier to read and maintain.

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

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

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

    [WebMethod]

    public string HelloWorld()

    {

      return "Hello World";

}

The VS .NET Web Service wizard added the [WebMethod] attribute to the HelloWorld() method. That declared HelloWorld as a web method. The ASP.NET runtime creates code for you in response to the presence of this attribute. The runtime created the Web Service Description Language (WSDL) document, which contains a description for the SOAP document that invokes the HelloWorld method. ASP.NET also adds support in the runtime to route SOAP requests to your HelloWorld method. In addition, the ASP.NET runtime dynamically creates HTML pages that enable you to test your new web service in IE. That's all in response to the presence of the WebMethod attribute. The attribute declared your intent, and the runtime ensured that the proper support was there. Using the attribute takes much less time and is much less 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.NET runtime uses reflection to determine which methods in your class are web methods. When they are found, the ASP.NET runtime can add all the necessary framework code to turn any function into a web method.

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

The [WebMethod] attribute is just one of many attributes that the .NET library defines that can help you create correct programs more quickly. A number of attributes help you create serializable types (see Item 25). As you saw in Item 4, attributes control conditional compilation. In those and other cases, you can create the code you need faster and with less chance for errors using declarative programming. You should use these .NET Framework attributes to declare your intent rather than write your own code. It takes less time, it's easier, and the compiler doesn't make mistakes.

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

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

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

    [DefaultSort("Name")]

    public class Customer

    {

        public string Name

        {

            get { return name; }

            set { name = value; }

        }

 

        public decimal CurrentBalance

        {

            get { return balance; }

        }

 

        public decimal AccountValue

        {

            get

            {

                return calculateValueOfAccount();

            }

        }

  }

The DefaultSort attribute e, the Name property. The implication is that any collection of Customers should be ordered by the customer name. The DefaultSort attribute is not part of the .NET Framework. To implement it, you need to create the DefaultSortAttribute class:

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

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

    public class DefaultSortAttribute : System.Attribute

    {

        private string name;

        public string Name

        {

            get { return name; }

            set { name = value; }

        }

 

        public DefaultSortAttribute(string name)

        {

            this.name = name;

        }

 }

You must still write the code to sort a collection of objects based on the presence of the DefaultSort attribute. You'll use reflection to find the correct property and then compare values of that property in two different objects. The good news is that you need to write this code only once.

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

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

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

    internal class GenericComparer : IComparer

    {

        // Information about the default property:

        private readonly PropertyDescriptor sortProp;

 

        // Ascending or descending.

        private readonly bool reverse = false;

 

        // Construct for a type

        public GenericComparer(Type t):this(t, false)

        {

        }

 

        // Construct for a type

        // and a direction

        public GenericComparer(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.

        int IComparer.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)

                return 1;

 

            if (sortProp == null)

                return 0;

 

            // Get the sort property from each object:

            IComparable lField = sortProp.GetValue(left) as IComparable;

            IComparable rField = sortProp.GetValue(right) as IComparable;

            int rVal = 0;

            if (lField == null)

                if (rField == null)

                    return 0;

                else

                    return -1;

            rVal = lField.CompareTo(rField);

            return (reverse) ? -rVal : rVal;

        }

}

The Generic comparer sorts any collection of Customers based on the property declared in the DefaultSort attribute:

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

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

The code to implement the GenericComparer makes use of advanced techniques, such as reflection (see Item 43). But you need to write it only once. From that point on, all you need to do is add the attribute to any class, and you can sort a collection of those objects using the generic comparer. If you change the parameter on the DefaultSort attribute, you change the class's behavior. You don't need to change any algorithms anywhere in your code.

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

This declarative idiom is useful to avoid writing repetitive code when a simple declaration can specify your intent. Look at the Generic Comparer class again. You could write different (and slightly simpler) versions of the sort algorithm for every type you created. The advantage to using declarative programming is that you can write one generic class and let a simple declaration create the behavior for each type. The key is that the behavior changes based on a single declaration, not based on any algorithm changes. The GenericComparer works for any type decorated with the DefaultSort attribute. If you need sorting functionality only once or twice in your application, write the simpler routines. However, if you might need the same behavior for many tens of different types in your program, the generic algorithm and the declarative solution will save you time and energy in the long run. You'd never write all the code generated by the WebMethod attribute. You should expand on that technique for your own algorithms. Item 42 discusses one example: how to use attributes to build add-on command handlers. Other examples might include anything from defining add-on packages to building dynamic web page UIs.

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值