中国IT动力,最新最全的IT技术教程
最新100篇 | 推荐100篇 | 专题100篇 | 排行榜 | 搜索 | 在线API文档 | 网通镜像
首 页 | 程序开发 | 操作系统 | 软件应用 | 图形图象 | 网络应用 | 精文荟萃 | 教育认证 | 硬件维护 | 未整理篇 | 站长教程
ASP JS PHP工程 ASP.NET 网站建设 UML J2EESUN .NET VC VB VFP 网络维护 数据库 DB2 SQL2000 Oracle Mysql
服务器 Win2000 Office C DreamWeaver FireWorks Flash PhotoShop 上网宝典 CorelDraw 协议大全 网络安全 微软认证
硬件维护  CPU  主板  硬盘  内存  显卡  显示器  键盘鼠标  声卡音箱  打印机  机箱电源  BIOS  网卡  C#  Java  Delphi  vs.net2005
  当前位置:> 程序开发 > 编程语言 > .NET > 临时文章
C#慢慢学(二)(e文转)
作者:佚名 时间:2004-04-10 09:14 出处:互连网 责编:chinaitpower
              摘要:C#慢慢学(二)(e文转)
1.14 Properties
A property is a named attribute associated with an object or a class. Examples of properties include the length of a string, the size of a font, the caption of a window, the name of a customer, and so on. Properties are a natural extension of fields – both are named members with associated types, and the syntax for accessing fields and properties is the same. However, unlike fields, properties do not denote storage locations. Instead, properties have accessors that specify the statements to execute in order to read or write their values. Properties thus provide a mechanism for associating actions with the reading and writing of an object’s attributes, and they furthermore permit such attributes to be computed.
The success of rapid application development tools like Visual Basic can, to some extent, be attributed to the inclusion of properties as a first-class element. VB developers can think of a property as being field-like, and this allows them to focus on their own application logic rather than on the details of a component they happen to be using. On the face of it, this difference might not seem like a big deal, but modern component-oriented programs tend to be chockfull of property reads and writes. Languages with method-like usage of properties (e.g., o.SetValue(o.GetValue() + 1);) are clearly at a disadvantage compared to languages that feature field-like usage  of properties (e.g., o.Value++;).
Properties are defined in C# using property declaration syntax. The first part of the syntax looks quite similar to a field declaration. The second part includes a get accessor and/or a set accessor. In the example below, the Button class defines a Caption property.
public class Button: Control
{
    private string caption;
    public string Caption {
        get {
            return caption;
        }
        set {
            caption = value;
            Repaint();
        }
    }
}
Properties that can be both read and written, like the Caption property, include both get and set accessors. The get accessor is called when the property’s value is read; the set accessor is called when the property’s value is written. In a set accessor; the new value for the property is given in an implicit value parameter.
Declaration of properties is relatively straightforward, but the true value of properties shows itself is in their usage rather than in their declaration. The Caption property can read and written in the same way that fields can be read and written:
Button b = new Button();
b.Caption = "ABC";        // set
string s = b.Caption;    // get
b.Caption += "DEF”;        // get & set
1.15 Indexers
If properties in C# can be likened to “smart fields”, then indexers can be likened to “smart arrays”. Whereas properties enable field-like access, indexers enable array-like access.
As an example, consider a ListBox control, which displays strings. This class wants to expose an array-like data structure that exposes the list of strings it contains, but also wants to be able to automatically update its contents when a value is altered. These goals can be accomplished by providing an indexer. The syntax for an indexer declaration is similar to that of a property declaration, with the main differences being that indexers are nameless (the “name” used in the declaration is this, since this is being indexed) and that additional indexing parameters are provided between square brackets.
public class ListBox: Control
{
    private string[] items;
    public string this[int index] {
        get {
            return items[index];
        }
        set {
            items[index] = value;
            Repaint();
        }
    }
}
As with properties, the convenience of indexers is best shown by looking at use rather than declaration. The ListBox class can be used as follows:
ListBox listBox = ...;
listBox[0] = "hello";
Console.WriteLine(listBox[0]);
1.16 Events
Events permit a class to declare notifications for which clients can attach executable code in the form of event handlers. Events are an important aspect of the design of class libraries in general, and of the system-provided class library in particular. C# provides an integrated solution for events.
A class defines an event by providing an event declaration, which looks quite similar to a field or event declaration but with an added event keyword. The type of this declaration must be a delegate type. In the example below, the Button class defines a Click event of type EventHandler.
public delegate void EventHandler(object sender, Event e);
public class Button: Control
{
    public event EventHandler Click;
    public void Reset() {
        Click = null;
    }
}
Inside the Button class, the Click member can be corresponds exactly to a private field of type EventHandler. However, outside the Button class, the Click member can only be used on the left hand side of the += and -= operators. This restricts client code to adding or removing an event handler. In the client code example below, the Form1 class adds Button1_Click as an event handler for Button1’s Click event. In the Disconnect method, the event handler is removed.
using System;
public class Form1: Form
{
    public Form1() {
        // Add Button1_Click as an event handler for Button1’s Click event
        Button1.Click += new EventHandler(Button1_Click);
    }
    Button Button1 = new Button();
    void Button1_Click(object sender, Event e) {
        Console.WriteLine("Button1 was clicked!");
    }
    public void Disconnect() {
        Button1.Click -= new EventHandler(Button1_Click);
    }
}
The Button class could be rewritten to use a property-like event declaration rather than a field-like event declaration. This change has no effect on client code.
public class Button: Control
{
        public event EventHandler Click {
            get {...}
            set {...}
        }
        public void Reset() {
            Click = null;
        }
}
1.17 Versioning
Versioning is an after-thought in most languages, but not in C#.
“Versioning” actually has two different meanings. A new version of a component is “source compatible” with a previous version if code that depends on the previous version can, when recompiled, work with the new version. In contrast, for a “binary compatible” component, a program that depended on the old version can, without recompilation, work with the new version.
Most languages do not support binary compatibility at all, and many do little to facilitate source compatibility. In fact, some languages contain flaws that make it impossible, in general, to evolve a class over time without breaking some client code.
As an example, consider the situation of a base class author who ships a class named Base. In this first version, Base contains no F method. A component named Derived derives from Base, and introduces an F. This Derived class, along with the class Base that it depends on, is released to customers, who deploy to numerous clients and servers.
// Author A
namespace A
{
    class Base    // version 1
    {
    }
}
// Author B
namespace B
{
    class Derived: A.Base
    {
        public virtual void F() {
            System.Console.WriteLine("Derived.F");
        }
    }
}
So far, so good. But now the versioning trouble begins. The author of Base produces a new version, and adds its own F method.
// Author A
namespace A
{
    class Base    // version 2
    {
        public virtual void F() {        // added in version 2
            System.Console.WriteLine("Base.F");
        }
    }
}
This new version of Base should be both source and binary compatible with the initial version. (If it weren’t possible to simply add a method then a base class could never evolve.)  Unfortunately, the new F in Base makes the meaning of Derived’s F is unclear. Did Derived mean to override Base’s F?  This seems unlikely, since when Derived was compiled, Base did not even have an F!  Further, if Derived’s F does override Base’s F, then does Derived’s F adhere to the contract specified by Base?  This seems even more unlikely, since it is pretty darn difficult for Derived’s F to adhere to a contract that didn’t exist when it was written. For example, the contract of Base’s F might require that overrides of it always call the base. Derived’s F could not possibly adhere to such a contract since it cannot call a method that does not yet exist.
In practice, will name collisions of this kind actually occur?  Let’s consider the factors involved. First, it is important to note that the authors are working completely independently – possibly in separate corporations – so no collaboration is possible. Second, there may be many derived classes. If there are more derived classes, then name collisions are more likely to occur. Imagine that the base class is Form, and that all VB, VC++ and C# developers are creating derived classes – that’s a lot of derived classes. Finally, name collisions are more likely if the base class is in a specific domain, as authors of both a base class and its derived classes are likely to choose names from this domain.
C# addresses this versioning problem by requiring developers to clearly state their intent. In the original code example, the code was clear, since Base did not even have an F. Clearly, Derived’s F is intended as a new method rather than an override of a base method, since no base method named F exists.
// Author A
namespace A
{
    class Base
    {
    }
}
// Author B
namespace B
{
    class Derived: A.Base
    {
        public virtual void F() {
            System.Console.WriteLine("Derived.F");
        }
    }
}
If Base adds an F and ships a new version, then the intent of a binary version of Derived is still clear – Derived’s F is semantically unrelated, and should not be treated as an override.
However, when Derived is recompiled, the meaning is unclear – the author of Derived may intend its F to override Base’s F, or to hide it. Since the intent is unclear, the C# compiler produces a warning, and by default makes Derived’s F hide Base’s F – duplicating the semantics for the case in which Derived is not recompiled. This warning alerts Derived’s author to the presence of the F method in Base. If Derived’s F is semantically unrelated to Base’s F, then Derived’s author can express this intent – and, in effect, turn off the warning – by using the new keyword in the declaration of F.
// Author A
namespace A
{
    class Base                    // version 2
    {
        public virtual void F() { // added in version 2
            System.Console.WriteLine("Base.F");
        }
    }
}
// Author B
namespace B
{
    class Derived: A.Base    // version 2a: new
    {
        new public virtual void F() {
            System.Console.WriteLine("Derived.F");
        }
    }
}
On the other hand, Derived’s author might investigate further, and decide that Derived’s F should override Base’s F, and clearly specify this intent through specification of the override keyword, as shown below.
// Author A
namespace A
{
    class Base                    // version 2
    {
        public virtual void F() { // added in version 2
            System.Console.WriteLine("Base.F");
        }
    }
}
// Author B
namespace B
{
    class Derived: A.Base    // version 2b: override
    {
        public override void F() {
            base.F();
            System.Console.WriteLine("Derived.F");
        }
    }
}
The author of Derived has one other option, and that is to change the name of F, thus completely avoiding the name collision. Though this change would break source and binary compatibility for Derived, the importance of this compatibility varies depending on the scenario. If Derived is not exposed to other programs, then changing the name of F is likely a good idea, as it would improve the readability of the program – there would no longer be any confusion about the meaning of F.
1.18 Attributes
C# is a procedural language, but like all procedural languages it does have some declarative elements. For example, the accessibility of a method in a class is specified by decorating it public, protected, internal, protected internal, or private. Through its support for attributes, C# generalizes this capability, so that programmers can invent new kinds of declarative information, specify this declarative information for various program entities, and retrieve this declarative information at run-time. Programs specify this additional declarative information by defining and using attributes.
For instance, a framework might define a HelpAttribute attribute that can be placed on program elements such as classes and methods to provide a mapping from program elements to documentation for them. The example
[AttributeUsage(AttributeTargets.All)]
public class HelpAttribute: System.Attribute
{
    public HelpAttribute(string url) {
        this.url = url;
    }
    public string Topic = null;
    private string url;
    public string Url {
        get { return url; }
    }
}
defines an attribute class named HelpAttribute, or Help for short, that has one positional parameter (string url) and one named argument (string Topic). Positional parameters are defined by the formal parameters for public constructors of the attribute class; named parameters are defined by public read-write properties of the attribute class. The square brackets in the example indicate the use of an attribute in defining the Help attribute. In this case, the AttributeUsage attribute indicates that any program element can be decorated with the Help attribute.
The example
[Help("http://www.mycompany.com/…/Class1.htm")]
public class Class1
{
    [Help("http://www.mycompany.com/…/Class1.htm", Topic ="F")]
    public void F() {}
}
shows several uses of the attribute.
Attribute information for a given program element can be retrieved at run-time by using the .NET runtime’s reflection support. The example
using System;
class Test
{
    static void Main() {
        Type type = typeof(Class1);
        object[] arr = type.GetCustomAttributes(typeof(HelpAttribute));
        if (arr.Length == 0)
            Console.WriteLine("Class1 has no Help attribute.");
        else {
            HelpAttribute ha = (HelpAttribute) arr[0];
            Console.WriteLine("Url = {0}, Topic = {1}", ha.Url, ha.Topic);
        }
    }
}
checks to see if Class1 has a Help attribute, and writes out the associated Topic and Url values if the attribute is pre
关闭本页
 
首页 | 投资与合作 | 服务条款 | 隐私政策 | 收藏本站 | 设为首页 | 新用户注册 | 免责声明 | 使用帮助
Copyright ©2005-2008 chinaitpower.com All rights reserved. www.chinaitpower.com 版权所有