Note on <C# 3.0 UNLEASHED With the .NET Framework 3.5> - 01

来源:互联网 发布:软件设计师考试报名费 编辑:程序博客网 时间:2024/05/01 01:23




Chapter 4: Understanding Reference Types and Value Types


What is Boxing?

Consider the below block:

decimal amountIn = 3.50m;object obj = amountIn; // box

Because both reference types and value types inherit object, you can assign any type to a variable of type object, any assignment to object is an implicit conversion, which is always safe. And boxing occurs when you assign a value type variable to a variable of type object.





What is Unboxing?

Doing an assignment from type object to a derived type may or may not be safe. C# forces you to state your intention with a cast operator, which is necessary because the C# compiler can’t tell whether the variable of object type is actually the target type of the assignment. Unboxing occurs when you assign a variable of type object to a variable with the same type as the true type of the object.

decimal amountIn = 3.50m;object obj = amountIn; // boxdecimal amountOut = (decimal)obj; // unbox


.NET Framework Class Library (FCL) included a library of collection classes, one of them being ArrayList. One of the features of these collections, including ArrayList, was that they could work generically with any type. The Unified Type System makes this possible because the collections operate on the object type, meaning that you can use them with any .NET type. But boxing occurs when you add a value type to a collection, which may cause serious impact on the performance if your collection holds an enormous number of elements.


Reference Type & Value Type


  1. A reference type variable will either hold a reference to an object on the heap or it will be set to the C# value null.
  2. The two places that a value type variable can be allocated is either the stack or along with a reference type on the heap.
  3. Value type variables passed as arguments to methods, as well as local variables defined inside a method, are pushed onto the stack with the method. However, if the value type variable is a field of a reference type object, it will be stored on the heap along with the reference type object.
  4. Regardless of memory allocation, a value type variable will always hold the object that is assigned to it. An uninitialized value type field will have a value  that defaults to some form of zero (bool defaults to false),
  5. C# 2.0 allow you to assign null to value types, because sometimes you receive values from external sources, such as XML files or databases that don’t have a value—they could be nil or null, respectively.
    DateTime? createDate = null;
    There are a couple ways to check a nullable type to see whether it has the value null. Here’s an example:
    bool isNull;isNull = createDate == null;isNull = createDate.HasValue;










Chapter 8: Designing Objects


The skeleton of a simple class's defination:


class WebSite{// constructors// destructors// fields// methods// properties// indexers// events// nested objects}


  • Constants are also static.
  • It is a good practice to implement integral constants as enum types, because that promotes a more strongly typed implementation.
  • Constants are initialized during compilation, and readonly fields are initialized during runtime, at the point that objects are instantiated.
    readonly DateTime currentDate = DateTime.Now;
  • Property:
    private string m_description;public string Description{get{return m_description;}set{m_description = value;}}
  • Auto-Implemented Properties:
    public int Rating { get; set; }
  • Indexers also have a parameter lis. The parameter list is delimited by brackets. Normally, parameter types are commonly int, so a class can provide array-like operations, but other useful parameter types are string or a custom enum.
    const int MinLinksSize = 10;const int MaxLinksSize = 10;string[] m_links = new string[MaxLinksSize];public string this[int i]{get{if (i >= MinLinksSize && i < MaxLinksSize){return m_links[i];}return null;}set{if (i >= MinLinksSize && i < MaxLinksSize){m_links[i] = value;}}}// code in another classstatic void Main(){WebSite site = new WebSite();site[0] = “http://www.mysite.com”;string link = site[0];}
  • The primary purpose of partial types is tool support in separating machine-generated code from the code you work with. The syntax identifying a partial type includes a class (or struct) definition with the partial modifier. At compile time, C# identifies all classes defined with the same name that have partial modifiers and compiles them into a single type.
    using System;partial class Program{static void Main(){m_someVar = 5;}}// Located in a different fileusing System;partial class Program{private static int m_someVar;}
  • Just use the static modifier. Subsequently, all members of the class must be static.
    public static class CustomMathLib{public static double DoAdvancedCalculation(double param1, double param2){return -1;}// other static members}



The System.Object Class

instance.GetType();  object.ReferenceEquals(instance, instance2);  instance.Equals(instance);
object.Equals(instance, instance2); you may need to override its behaviorinstance.GetHashCode();  instance.GetCopy();Cloning ObjectsCalling GetCopy actually invokes MemberwiseClone(), which does just a shallow copy. A shallow copy will only copy objects at the first level of the object graph.instance.ToString(); you may need to override its behavior






Chapter 9: Designing Object-Oriented Programs


TERM: All classes have full access to their own members without qualification. Qualification refers to using a class name with the dot operator to access a class member—MyObject.SomeMethod, for instance.


Hiding Base Class Members

So just exactly as the case described in the book, you implicitly override a base class's member method:

namespace Chapter_09{class Contact{public string FullAddress() {}}class SiteOwner : Contact{public string FullAddress(){// return different address}}class Program{static void Main(string[] args){Contact myContactAsSiteOwner = new SiteOwner();string address = myContactAsSiteOwner.FullAddress(); // it actually invokes Contact.FullAddress()}}}


First of all, you will get a warning as shown in the following screenshot:



And in addition, the only tricky thing worth noticing isif you instantiate an object of a derived class type, and assign it to a variable declared as its base class type, and when you call the method through the object, what will be invoked is the one defined on the base class. So, if you want to hide something in base class, do it explicitly.


What does new modifier do here?

class SiteOwner : Contact
{
public new string FullAddress() {}
}

Putting a new modifier here won't change any behavior of the code, it just eliminates the warning message.

"Placing the new modifier in front of the derived class member states that the developers know there is a base class method with the same name, and they definitely want to hide that member. This prevents breakage of existing code that depends on the implementation of the derived class member. With C#, the compile-time type of a variable determines which nonvirtual method is called.
The compile-time type of myContactAsSiteOwner is Contact, so calling FullAddress on the instance invokes Contact.FullAddress. The runtime type, defined via the new modifier on myContactAsSiteOwner is SiteOwner,but that doesn’t matter, since FullAddress is not virtual."


There is another option, polymorphism, which is discussed next.


Sealed Classes

A class declared with the sealed modifier cannot be inherited.


Access Modifiers

  • Internal modifier restricts a class visibility to only the codewithin the same assembly.
  • protected modifier combined withinternal modifier together indicate thata class is accessible to the code within the same assembly and the classes inheriting it, even those are not in the same assembly
  • If you omit modifiers when defining classes, enums and structs,they will be internal by default. In addition,you cannot mark an instance as public if it is of a class type which is declared as internal.


Polymorphism

Use modifier virtual in base class and override in derived class to explicitly support polymorphism.

"C# will ensure that overrides (runtime types) of virtual (compile-time types) methods are called when the method on the compile-time type object is called."


C# permits polymorphism with property and indexer.










Chapter 11: Error and Exception Handling


According to the code example in section of 'Recovering from Exceptions', there are some thing to highlight about the control flow of Exception:

  • Even if there is a break statement inside a loop block,the finally block below that will still be executed once the execution goes into the containing block.
  • Handled exception won't be propagated unless you explicitly throw it again in catch block.
  • finally block at each layer in the exception handling chain will be executed, regardless of whether there is an exception, don't forget it.



The sample code in 'Recovering from Exceptions' section:

In order to better understand what happened in each iteration of the do-while loop, we can make a little change:

StreamWriter sw = new StreamWriter("exceptions.txt", true);


That's it, when instantiating a StreamWriter object, use another constructor, so that it will append the string into the .txt file, instead of overwriting its content. We can find the file under ./bin/Debug/

Byte 1: 0Byte 2: 1Byte 3: 2Byte 4: 3CloseByte 1: 0Byte 2: 1Byte 3: 2Byte 4: 3CloseByte 1: 0Byte 2: 1Byte 3: 2Close



Designing Your Own Exception

public class TooManyItemsException : ApplicationException{public TooManyItemsException() : base(@"**TooManyItemsException**  You added too many items to this container. Try specifying a smaller number or increasing the container size."){}}

The reason we heve to use this convoluted approach to set the Message property is because it is read-only, we cannot just initialize its value during the initialization of TooManyItemsException. Hence we use base class initialization by calling the ApplicationException class constructor that accepts a string.


Checked and Unchecked Statements


A program is always running in a checked or an unchecked state. During runtime, the checking context for nonconstant expressions is determined by the environment in which your program is running. The default for the C# compiler in the Microsoft .NET Framework SDK is unchecked. Constant expressions are always in a checked context. Here’s an example:

Total = 25000000 * 15000000; // compiler error











Chapter 14: Implementing Abstract Classes and Interfaces







Chapter 15: Managing Object Lifetime


Multiple constructors:

public class WebSite{string siteName;string url;string description;// Constructorspublic WebSite(): this("No Site", "no.url", "No Description") {}public WebSite(string newSite): this(newSite, "no.url", "No Description") {}public WebSite(string newSite, string newURL): this(newSite, newURL, "No Description") {}public WebSite(string newSite, string newURL, string newDesc){siteName = newSite;url = newURL;description = newDesc;}}


After the constructor name is a colon and the keyword this. The this keyword refers to the current instance of an object. When used with a constructor declaration, the this keyword calls another constructor of the current instance.


In class initialization, there is a defined order of initialization, as follows:

  1. Class field initializers. This guarantees they can be initialized before one of the constructors tries to access them.
  2. Other constructors called with the this operator.
  3. Statements within the constructor’s block.


Default constructor:

It is always a good idea to provide a default parameterless constructor, in case that someone calls initiate its instance without argument.


Static constructor:

Static constructor will be invoked when the class is loaded, and that happen only once.Static constructor can only access static field.

Static classes are loaded before any instance of an object can be created.


The sequence of operations when a static constructor is invoked

  1. The type is loaded before the first instance is created.
  2. The type is loaded prior to accessing static members.
  3. The type is loaded ahead of any derived types.
  4. The type is loaded only one time per program execution.


Private constructor:

Private constructor can be useful if you want to prevent it from being instantiate.


Object Initializers

With definition:

class StaffMember{public string Name { get; set; }}class MedicalOffice{public StaffMember Doctor { get; set; }public StaffMember LeadNurse { get; set; }public StaffMember AssistantNurse { get; set; }public StaffMember Intern1 { get; set; }public StaffMember Intern2 { get; set; }public StaffMember Intern3 { get; set; }public StaffMember Intern4 { get; set; }}

You can initialize them like:

var staffMemberNew = new StaffMember { Name = "Joe" };

And even nest them:

var medOff = new MedicalOffice{Doctor = new StaffMember(){Name = “Marcus”},LeadNurse = new StaffMember{Name = “Nancy”}};


Object Finalization

... because of the way the CLR GC handles objects, there is the distinct possibility of a finalizer not being called. Furthermore, there is no precise time after which your object is destroyed that a finalizer is called, which causes problems in scalability.

Using a finalizer to clean up class resources increases the risk of resource depletion, system corruption, or deadlock. The basic rule is this: Don’t depend on a finalizer to release critical resources being held by a class.


Automatic Memory Management


Memory Allocation



Inside the Garbage Collector

Determining which objects are collectable involves a process of creating a graph of live objects and, after all objects have been visited, cleaning up objects that are not in the graph.


GC Optimization




Proper Resource Cleanup


Class instances with finalizers are more expensive to clean up than class instances without finalizers. This is because the GC has to make two passes for objects with finalizers: The first pass executes the finalizer, and the second pass cleans up the object.


There is no way to tell when the garbage collection will happen. Some garbage collection methods provide information and allow you to force a garbage collection, but the benefitto-effort ratio is so small that they would be a waste of time.


There are also scenarios where the GC might not run at all, such as a program crash.


The Dispose Pattern



The using Statement

Hand in hand with the IDisposable interface is the using statement. The parameter to the using statement must be an IDisposable object; otherwise, a compile-time error will occur.


The using Statement accomplishes the exact same thing as the try/finally block. In fact, it produces Intermediate Language (IL) code equivalent to the try/finally block. This is the preferred way to handle IDisposable objects, and you would use a try/finally only if an object isn't IDisposable.


Interacting with the Garbage Collector



0 0
原创粉丝点击