您的位置:首页 > 编程语言 > C#

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

2015-05-11 21:02 736 查看


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; // box
decimal 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

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.
The two places that a value type variable can be allocated is either the stack or along with a reference type on the heap.
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.
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),
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 class
static 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 file
using 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 behavior
instance.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: 0
Byte 2: 1
Byte 3: 2
Byte 4: 3
Close
Byte 1: 0
Byte 2: 1
Byte 3: 2
Byte 4: 3
Close
Byte 1: 0
Byte 2: 1
Byte 3: 2
Close


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;
// Constructors
public 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:

Class field initializers. This guarantees they can be initialized before one of the constructors tries to access them.
Other constructors called with the this operator.
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

The type is loaded before the first instance is created.
The type is loaded prior to accessing static members.
The type is loaded ahead of any derived types.
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: