Vam's Notes

Just notes. But notes for everything.

导航

.Net study notes of "Professional C# 2005" - Appendix A

  • Therefore, instance methods, just like static methods, are stored only once, and associated with the class as a whole.
  • A quick look at the documentation reveals that no overload of WriteLine() takes short. So what will the compiler do? In principle, it could generate code that converts short to int and call the int version of Console.WriteLine(). Or it could convert short to long and call Console.WriteLine(long). It could even convert short to string.

    In this situation, each language will have a set of rules for what conversion will be the one that is actually performed (for C#, the conversion to int is the preferred one). However, you can see the potential for confusion. For this reason, if you define method overloads, you need to take care to do so in a way that won't cause any unpredictable results.
  • implementation inheritance & interface inheritance.
  • This must always be the case; because when you code the base class, you don't necessarily know what other derived classes might be added in the future — and you wouldn't want your code to be broken when someone adds a derived class!
  • A large application will not have just one hierarchy but will typically implement a large number of hierarchies that possibly stretches into hundreds of classes. That may sound daunting, but the alternative, before object-oriented programming came into being, was to have literally thousands of functions making up your program, with no way to group them into manageable units. Classes provide a very effective way of breaking your program into smaller sections. This not only facilitates maintenance but also makes your program easier to understand because the classes represent the actual objects that your program is representing in a very intuitive way.
    It's also important with your classes to think carefully about the separation between the public interface presented to client code and the private internal implementation. In general, the more of a class you are able to keep private, the more modular your program will become, in the sense that you can make modifications or improvements to the internal implementation of one class and be certain that it will not break or even have any effect on any other part of the program.
  • You've now ensured that you can mix different types of classes in one array, but this will now give the compiler a new problem. Suppose you have a snippet of code like this:

    1Customer aCustomer;
    2// Initialize aCustomer to a particular tariffa
    3Customer.RecordCall(TypeOfCall.CallToLandline, 20);


    What the compiler can see is a Customer reference, and you are to call the RecordCall() method on it. The trouble is that aCustomer might refer to a Customer instance, or it might refer to a Nevermore60Customer instance, or it might refer to an instance of some other class derived from Customer. Each of these classes might have its own implementation of RecordCall(). How will the compiler determine which method should be called? There are two answers to this, depending on whether the method in the base class is declared as virtual and the derived class method as an override:
      • If the methods are not declared as virtual and override, respectively, the compiler will simply use the type that the reference was declared to be. In this case, because aCustomer is of type Customer, it will arrange for the Customer.RecordCall() method to be called, no matter what aCustomer is actually referring to.
      • If the methods are declared as virtual and override, respectively, the compiler will generate code that checks what the aCustomer reference is actually pointing to at runtime. It then identifies which class this instance belongs to and calls the appropriate RecordCall() override. This determination of which overload should be called will need to be made separately each time the statement is executed. For example, if the virtual method call occurs inside a foreach loop that executes 100 times, on each iteration through the loop the reference might be pointing to a different instance and therefore to a different class of object.
  • If any method in a class is abstract, that implies the class itself should be abstract, and the compiler will raise an error if the class is not so declared. Also, any non-abstract class that is derived from this class must override the abstract method. These rules prevent you from ever actually instantiating a class that doesn't have implementations of all its methods.
  • In general, an interface is a contract that says that a class must implement certain features (usually methods and properties), but which doesn't specify any implementations of those methods and properties.
  • Beyond the lack of method implementations, the main point to note is the lack of any modifiers on the members. Interface members are always public and cannot be declared as virtual or static.
  • So why have interfaces? Up to now you've treated classes as having certain members and not concerned yourself about grouping any members together — our classes have simply contained a list of various miscellaneous methods, fields, properties, and so on. There are often situations in which you need to know that the class implements certain features in order to be able to use a class in a certain way. An example is provided by the foreach loop in C#. In principle, it is possible to use foreach to iterate through a class instance, provided that that class is able to act as if it is a collection. How can the .NET runtime tell whether a class instance represents a collection? It queries the instance to find out whether it implements the System.Collections.IEnumerable interface. If it does, the runtime uses the methods on this interface to iterate through the members of the collection. If it doesn't, foreach will raise an exception.
  • This achieves the same effect — specifying how to initialize each object without explicitly indicating a constructor. Indeed, you have already done something like this in all the Authenticator samples, in which you specified that the password field should automatically be initialized to an empty string. The answer is that here we are trying to introduce the concept of a constructor. The preceding code is really just C# shorthand for specifying construction code implicitly — a shorthand that is specific to C#. Behind this shorthand there is still a constructor at work. Besides, by writing a constructor explicitly, it means you can write code to compute initial values at runtime — the shorthand requires values to be known at compile time, as constants.
  • It's not necessary to provide a constructor for your class — you haven't supplied one for any of the examples so far. In general, if you don't explicitly supply any constructor, the compiler will just make up a default one for you behind the scenes. It'll be a very basic constructor that just initializes all the member fields to their normal default values (empty string for strings, zero for numeric data types, and false for bools).
    Note
    Initializing to default values is something that happens in C# because C# initializes all members of a class. If you are coding in a different language, this behavior might differ. For example, by default C++ never initializes anything unless you explicitly indicate that's what you want. So in C++, if you don't supply a constructor to a class, its members won't get initialized to anything (unless they have constructors instead).

 

posted on 2007-05-16 12:52  Vam  阅读(204)  评论(0编辑  收藏  举报