using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
LoginInfoUserControl loginControl = FindControl<LoginInfoUserControl>(this.Page, "ucLogin");
}
public static T FindControl<T>(Control root, string controlID) where T : class
{
if (root is T && root.ID == controlID)
{
return root as T;
}
foreach (Control control in root.Controls)
{
T foundControl = FindControl<T>(control, controlID);
if (foundControl != null)
{
return foundControl;
}
}
return null;
}
}
Thursday, March 15, 2007
ASP.NET Find Control Recursively
Sometimes we need to get a control instance inside a page. Looking up each control recursively is the easiest way:
Wednesday, March 07, 2007
HttpWebRequest With Cookie Context
I was doing some load test on an ASP.NET page. The page uses Cache to store some profile data, and the Cache is associated with the client Cookie value. The load test tools I have don't have such dynamic cookie assignment functionality. So I created a simple load test tool for this. The key point is to assign the CookieContainer property of the HttpWebRequest object:
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
namespace HttpRequestTest
{
class Program
{
static void Main(string[] args)
{
string url = "http://localhost/MyDashBoard.aspx";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
CookieContainer cookieContainer = new CookieContainer();
Cookie cookie = new Cookie("cookieKey", "cookieValue");
cookie.Path= "/";
cookie.Domain = "localhost";
cookieContainer.Add(cookie);
request.CookieContainer = cookieContainer;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream responseData = response.GetResponseStream();
StreamReader reader = new StreamReader(responseData);
string responseFromServer = reader.ReadToEnd();
Console.Read();
}
}
}
Thursday, February 22, 2007
ICollection<T>, IList<T>, CollectionBase, Collection<T> And List<T>
.NET provides all these interfaces and classes. What is the difference between them?
They all inherit/implement IEnumerable interface, and support simple iteration over the collection.
ICollection<T> interface is the simplest one. It only inherits IEnumerable interface and includes just a few members.
IList<T> interface inherits ICollection<T> interface, and it offers you the ability to index an element. So go IList<T> if you need indexing.
CollectionBase is an abstract class inheriting from ICollection and IList interfaces. It's a recommanded way of using it to implement a custom collection before .NET 2.0, derive a class from it and you get all functions implemented by CollectionBase.
Collection<T> is a concrete implementation of ICollection<T> and IList<T>. It's better than CollectionBase because it's a generic collection and supports all new generic features.
List<T> class also implements both ICollection<T> and IList<T>. It has more power than Collection<T>. Collection<T> exposes about 10 methods, while List<T> exposes more than 40 methods. You are able to do sorting and search by default with List<T>.
List<T> has more functions, but too much implementation limits its extensibility. In general, use Collection<T> for public APIs, and use List<T> for internal containers.
An example of Collection<T> more extensible than List<T> is that Collecton<T> has virtual methods so you can override them and have your own handling logic when an item is inserted and updated inside the container:
Related interface/class definition:
They all inherit/implement IEnumerable interface, and support simple iteration over the collection.
ICollection<T> interface is the simplest one. It only inherits IEnumerable interface and includes just a few members.
IList<T> interface inherits ICollection<T> interface, and it offers you the ability to index an element. So go IList<T> if you need indexing.
CollectionBase is an abstract class inheriting from ICollection and IList interfaces. It's a recommanded way of using it to implement a custom collection before .NET 2.0, derive a class from it and you get all functions implemented by CollectionBase.
Collection<T> is a concrete implementation of ICollection<T> and IList<T>. It's better than CollectionBase because it's a generic collection and supports all new generic features.
List<T> class also implements both ICollection<T> and IList<T>. It has more power than Collection<T>. Collection<T> exposes about 10 methods, while List<T> exposes more than 40 methods. You are able to do sorting and search by default with List<T>.
List<T> has more functions, but too much implementation limits its extensibility. In general, use Collection<T> for public APIs, and use List<T> for internal containers.
An example of Collection<T> more extensible than List<T> is that Collecton<T> has virtual methods so you can override them and have your own handling logic when an item is inserted and updated inside the container:
protected virtual void ClearItems();Some people argue that interface is better because it only provides the contract and the detailed implementation is not exposed, and returning IList<T> or ICollection<T> are better Collection<T> or List<T>, in addition you can not inherit your custom class once it deriving from concrete class like collection<T> or List<T>. That's true, but other people debate that returning interface without functions to end users is useless, what's the point you expose a collection of objects without some basic functions like search and sorting? That's also valid. It really depends on the real situation and use case.
protected virtual void InsertItem(int index, T item);
protected virtual void RemoveItem(int index);
protected virtual void SetItem(int index, T item);
Related interface/class definition:
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
public interface IEnumerable<T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}
public interface ICollection<T> : IEnumerable<T>, IEnumerable
{
bool IsReadOnly { get; }
void Add(T item);
void Clear();
bool Contains(T item);
void CopyTo(T[] array, int arrayIndex);
bool Remove(T item);
}
public interface IList<T> : ICollection<T>, IEnumerable<T>, IEnumerable
{
T this[int index] { get; set; }
int IndexOf(T item);
void Insert(int index, T item);
void RemoveAt(int index);
}
public abstract class CollectionBase : IList, ICollection, IEnumerable
{
protected CollectionBase();
protected CollectionBase(int capacity);
public int Capacity { get; set; }
public int Count { get; }
protected ArrayList InnerList { get; }
protected IList List { get; }
public void Clear();
public IEnumerator GetEnumerator();
protected virtual void OnClear();
protected virtual void OnClearComplete();
protected virtual void OnInsert(int index, object value);
protected virtual void OnInsertComplete(int index, object value);
protected virtual void OnRemove(int index, object value);
protected virtual void OnRemoveComplete(int index, object value);
protected virtual void OnSet(int index, object oldValue, object newValue);
protected virtual void OnSetComplete(int index, object oldValue, object newValue);
protected virtual void OnValidate(object value);
public void RemoveAt(int index);
}
public class Collection<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable
{
public Collection();
public Collection(IList<T> list);
public int Count { get; }
protected IList<T> Items { get; }
public T this[int index] { get; set; }
public void Add(T item);
public void Clear();
protected virtual void ClearItems();
public bool Contains(T item);
public void CopyTo(T[] array, int index);
public IEnumerator<T> GetEnumerator();
public int IndexOf(T item);
public void Insert(int index, T item);
protected virtual void InsertItem(int index, T item);
public bool Remove(T item);
public void RemoveAt(int index);
protected virtual void RemoveItem(int index);
protected virtual void SetItem(int index, T item);
}
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable
{
public List();
public List(IEnumerable<T> collection);
public List(int capacity);
public int Capacity { get; set; }
public int Count { get; }
public T this[int index] { get; set; }
public void Add(T item);
public void AddRange(IEnumerable<T> collection);
public ReadOnlyCollection<T> AsReadOnly();
public int BinarySearch(T item);
public int BinarySearch(T item, IComparer<T> comparer);
public int BinarySearch(int index, int count, T item, IComparer<T> comparer);
public void Clear();
public bool Contains(T item);
public List<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter);
public void CopyTo(T[] array);
public void CopyTo(T[] array, int arrayIndex);
public void CopyTo(int index, T[] array, int arrayIndex, int count);
public bool Exists(Predicate<T> match);
public T Find(Predicate<T> match);
public List<T> FindAll(Predicate<T> match);
public int FindIndex(Predicate<T> match);
public int FindIndex(int startIndex, Predicate<T> match);
public int FindIndex(int startIndex, int count, Predicate<T> match);
public T FindLast(Predicate<T> match);
public int FindLastIndex(Predicate<T> match);
public int FindLastIndex(int startIndex, Predicate<T> match);
public int FindLastIndex(int startIndex, int count, Predicate<T> match);
public void ForEach(Action<T> action);
public List<T>.Enumerator GetEnumerator();
public List<T> GetRange(int index, int count);
public int IndexOf(T item);
public int IndexOf(T item, int index);
public int IndexOf(T item, int index, int count);
public void Insert(int index, T item);
public void InsertRange(int index, IEnumerable<T> collection);
public int LastIndexOf(T item);
public int LastIndexOf(T item, int index);
public int LastIndexOf(T item, int index, int count);
public bool Remove(T item);
public int RemoveAll(Predicate<T> match);
public void RemoveAt(int index);
public void RemoveRange(int index, int count);
public void Reverse();
public void Reverse(int index, int count);
public void Sort();
public void Sort(Comparison<T> comparison);
public void Sort(IComparer<T> comparer);
public void Sort(int index, int count, IComparer<T> comparer);
public T[] ToArray();
public void TrimExcess();
public bool TrueForAll(Predicate<T> match);
public struct Enumerator : IEnumerator<T>, IDisposable, IEnumerator
{
public T Current { get; }
public void Dispose();
public bool MoveNext();
}
}
Friday, February 09, 2007
Binding Enum Type In .NET
Enum type is not bindable to list controls such as DropDownList and CheckBoxList, because Enum is not IEnumerable, IListSource or IDataSource type. One solution is put enum data to an IEnumerable collection such as generic dictionary, and then bind the list control to that collection. Following code demos such usage:
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
BindableEnum.BindListControl<Status>(DropDownList1);
}
}
public enum Status
{
NotAvailable = 0,
Pending = 3,
InProgress = 4,
Completed = 5,
Aborted = 6
}
public static class BindableEnum
{
public static void BindListControl<TEnum>(ListControl list)
{
if (typeof(TEnum).BaseType != typeof(Enum))
return;
Dictionary<string, string> enumDict = new Dictionary<string, string>();
string[] names = Enum.GetNames(typeof(TEnum));
for (int i = 0; i < names.Length; i++)
{
enumDict.Add(names[i], ((int)Enum.Parse(typeof(TEnum), names[i])).ToString());
}
list.DataSource = enumDict;
list.DataTextField = "Key";
list.DataValueField = "Value";
list.DataBind();
}
}
Tag:
C#
Thursday, January 18, 2007
Automatic Properties Issue On Structs
In general, class objects are allocated on the heap while structs are created on the stack, reference vs. value for short. Their syntax is almost identical, but I found one issue using struct today. Following C# code doesn't compile in Visual Studio 2005:
struct GeoCodeThe error is:
{
public double Longitude { get; set; }
public double Latitude { get; set; }
public GeoCode(double longitude, double latitude)
{
this.Longitude = longitude;
this.Latitude = latitude;
}
}
Backing field for automatically implemented property 'Program.GeoCode.Latitude' must be fully assigned before control is returned to the caller. Consider calling the default constructor from a constructor initializer ...
The 'this' object cannot be used before all of its fields are assigned to ...It's okay with:
struct GeoCodeSounds like the issue of automatic properties on structs in .NET 2.0. To fix it, just follow the error description "Consider calling the default constructor from a constructor..":
{
public double Longitude;
public double Latitude;
public GeoCode(double longitude, double latitude)
{
this.Longitude = longitude;
this.Latitude = latitude;
}
}
struct GeoCode
{
public double Longitude { get; set; }
public double Latitude { get; set; }
public GeoCode(double longitude, double latitude) : this()
{
this.Longitude = longitude;
this.Latitude = latitude;
}
}
Friday, January 05, 2007
A tricky int i=0; i=i++; i = ? question
I was asked a question that sounds simple but it's a bit tricky. What's the output of following C# code?
Why? i++ is an after operation, so a temporary value is created to store the before and after value. The explanation is not that straightforward. Let's look at the IL code generated by compiler, and see what's under the hood:
(Updated Jan 7, 07) I tested in Java (1.5) and got the same result of "0 1". But in C++ (compiled by VS.NET 2005) it returns "1 1".
public class ProgramI thought it's "1 1" but I was wrong. The correct answer is "0 1".
{
static void Main(string[] args)
{
int i = 0; i = i++;
int j = 0; j = ++j;
Console.WriteLine("{0} {1}", i, j);
Console.Read();
}
}
Why? i++ is an after operation, so a temporary value is created to store the before and after value. The explanation is not that straightforward. Let's look at the IL code generated by compiler, and see what's under the hood:
.method private hidebysig static void Main(string[] args) cil managedWe can see that the plus operation is run in a separate stack, the result of 1 is assigned to the local variable, but is overwritten by original value of 0. Thus the original i value of 0 is used for the assignment of i = i++. Is it ambiguous? I'm not sure. It's the way how .NET deals with such scenarios.
{
.entrypoint
// Code size 47 (0x2f)
.maxstack 3
.locals init (int32 V_0,
int32 V_1)
IL_0000: nop
// i operation
IL_0001: ldc.i4.0 // Put constant number 0 onto the stack
IL_0002: stloc.0 // Pop from the stack and store it in the local variable #0 (number = 0)
IL_0003: ldloc.0 // Push local variable #0 onto the stack
IL_0004: dup // Duplicate the value in the stack (push another 0 onto stack)
IL_0005: ldc.i4.1 // Put constant number 1 onto the stack
IL_0006: add // Pop last two values from stack and push back their sum (1+0=1)
IL_0007: stloc.0 // Pop from stack and store it to local variable #0 (number=1)
IL_0008: stloc.0 // Pop from stack and store it to local variable #0 (overwritten number=0)
// j operation
IL_0009: ldc.i4.0 // put constant number 0 onto the stack
IL_000a: stloc.1 // Pop from the stack and store it in the local variable #1 (number=0)
IL_000b: ldloc.1 // Push local variable #1 onto the stack
IL_000c: ldc.i4.1 // Put constant number 1 on the stack
IL_000d: add // Pop last two values from stack and push back their sum (1+0=1)
IL_000e: dup // Duplicate the value in the stack (push another 1 onto stack)
IL_000f: stloc.1 // Pop from the stack and store it to local variable #1 (number=1)
IL_0010: stloc.1 // Pop from the stack and store it to local variable #1 (number=1)
IL_0011: ldstr "{0} {1}"
IL_0016: ldloc.0 // Load local variable #0 (0) onto stack
IL_0017: box [mscorlib]System.Int32
IL_001c: ldloc.1 // Load local variable #1 (1) onto stack
IL_001d: box [mscorlib]System.Int32
IL_0022: call void [mscorlib]System.Console::WriteLine(string,
object,
object)
IL_0027: nop
IL_0028: call int32 [mscorlib]System.Console::Read()
IL_002d: pop
IL_002e: ret
} // end of method Program1::Main
(Updated Jan 7, 07) I tested in Java (1.5) and got the same result of "0 1". But in C++ (compiled by VS.NET 2005) it returns "1 1".
Tag:
.NET
Tuesday, November 21, 2006
Passing Multiple-value With XML In SQL Server 2005
The XML functions has been improved a lot in SQL Server 2005. Suppose there's a simple Contacts table:
Contacts(Name varchar(100), Phone varchar(100), Email varchar(100))The application is sending following XML data to SQL Server for Contacts update (simply string type for ADO.NET parameter):
<Contacts>The stored procedure takes the XML as parameter and parse it internally:
<Contact>
<Name>Name ABC</Name>
<Phone>111-222-3333</Phone>
<Email>abc@abc.com</Email>
</Contact>
<Contact>
<Name>Name BCD</Name>
<Phone>222-333-4444</Phone>
<Email>bcd@bcd.com</Email>
</Contact>
</Contacts>
CREATE PROCEDURE [dbo].[Update_ContactsByXml]One advantage of using XML to pass multi-value is that it's not vulnerable to SQL injection attack.
(
@Contacts xml
)
AS
BEGIN
SET NOCOUNT ON
-- Create a temp table to hold the values
declare @tmpItems table
(
Name varchar(100),
Phone varchar(100),
Email varchar(100)
)
-- Insert all Ids to temp table
INSERT INTO @tmpItems
SELECT TMPXML.Nodes.value('./Name[1]', 'varchar(100)') as Name,
TMPXML.Nodes.value('./Phone[1]', 'varchar(100)') as Phone,
TMPXML.Nodes.value('./Email[1]', 'varchar(100)') as Email
FROM @Contacts.nodes('//Contacts/Contact') TMPXML (Nodes)
-- Update existing contacts
UPDATE C SET Phone = T.Phone, Email = T.Email
FROM @tmpItems T INNER JOIN Contacts C ON T.Name = C.Name
-- Insert new contacts
INSERT INTO Contacts
SELECT * FROM @tmpItems WHERE Name NOT IN (SELECT Name FROM Contacts)
END
Tag:
Database
Tuesday, November 07, 2006
A Few Releases
After long time of Beta the final release date of Java 6 (1.6) is confirmed to be released next month, which is a big news for Java community. In the Linux world, Fedora Core 6 was released last month, and its previous version Fedora Core 5 was released less than 7-month ago. Fedora looks like trying to catch up the installation/UI improvement brought by Ubuntu, who announced latest 6.10 release two weeks ago.
I used to work with C/Java in Unix/Linux, but most of my work in recent years is with Microsoft solutions, .NET and C# specifically. Now I pay more attention on MS technologies. So what's new in Microsoft world? Well Microsoft has long product lines, and new releases or updates are just too frequent: Team Foundation Server (TFS) 2006, BizTalk 2006, Office 2007, Windows Server 2008, etc...
What else? Microsoft has just released .NET 3.0 and announced the completion of SharePoint 2007 RTM today!
What's new in .NET 3.0? Mainly four components are added and Microsoft named them foundations:
I happen to have a chance to take some BizTalk 2006/SharePoint 2007 courses during the past 6 months. The course materials were based on the SharePoint 2007 Beta. I am very impressive of how SharePoint technologies can help a business to streamline line their process. Common IT tasks in company, such as creating/editing pages, adding/configuring web parts, configuring people permissions, document management, team collaborations, etc., now can be easily done by business people without IT guys' assistance.
Well you would say, what about developers and IT specialists? In this radically changing world, we need to learn more and more, right? True. New releases and new products indicate changes and new stuff. Change can be scary and you have to face it. But no matter C, Java or C#, Windows or Linux, your existing skills can transition in new technologies in some forms. Those fancy technologies and buzz words can be easily adopted since you have the fundamental of understanding how the computer world works. People living in this world all face too much information, especially in IT world. It would be great if we could filter all information around us efficiently.
Is there a course of "IT 101 - How To Avoid Being Overwhelmed" in school?
I used to work with C/Java in Unix/Linux, but most of my work in recent years is with Microsoft solutions, .NET and C# specifically. Now I pay more attention on MS technologies. So what's new in Microsoft world? Well Microsoft has long product lines, and new releases or updates are just too frequent: Team Foundation Server (TFS) 2006, BizTalk 2006, Office 2007, Windows Server 2008, etc...
What else? Microsoft has just released .NET 3.0 and announced the completion of SharePoint 2007 RTM today!
What's new in .NET 3.0? Mainly four components are added and Microsoft named them foundations:
- Windows Presentation Foundation (WPF): a new user interface subsystem and API based on XML and vector graphics, which uses 3D computer graphics hardware and Direct3D technologies.
- Windows Communication Foundation (WCF): a service-oriented messaging system which allows programs to interoperate locally or remotely similar to web services.
- Windows Workflow Foundation (WF): a workflow engine for task automation and integrated transactions.
- Windows CardSpace: a component securely storing a person's digital identities and provides a unified interface for choosing the identity for a particular transaction, such as logging in to a website.
I happen to have a chance to take some BizTalk 2006/SharePoint 2007 courses during the past 6 months. The course materials were based on the SharePoint 2007 Beta. I am very impressive of how SharePoint technologies can help a business to streamline line their process. Common IT tasks in company, such as creating/editing pages, adding/configuring web parts, configuring people permissions, document management, team collaborations, etc., now can be easily done by business people without IT guys' assistance.
Well you would say, what about developers and IT specialists? In this radically changing world, we need to learn more and more, right? True. New releases and new products indicate changes and new stuff. Change can be scary and you have to face it. But no matter C, Java or C#, Windows or Linux, your existing skills can transition in new technologies in some forms. Those fancy technologies and buzz words can be easily adopted since you have the fundamental of understanding how the computer world works. People living in this world all face too much information, especially in IT world. It would be great if we could filter all information around us efficiently.
Is there a course of "IT 101 - How To Avoid Being Overwhelmed" in school?
Tag:
Other
Sunday, October 15, 2006
.NET TableAdapters Inside TransactionScope
Both TableAdapter and TransactionScope are new in .NET 2.0. Auto-generated TableAdapter and DataTable could play an ORM (Object-relational Mapping) role and act as DAL (Data Access Layer) in an application. TransactionScope wraps the complexity of transaction, and it allows you to put DB interactions inside a transactional block. Using TableAdapter and TransactionScope properly can significantly simplify developers work.
I have just read an article Managing Transactions using TransactionScope that provides an example of using TableAdapter and TransactionScope together:
Looks really great and simple. But there's an issue of such implementation: Distributed Transaction Coordinator (DTC) is promoted at run time to complete this transaction. A run time error page will show up if DTC is not properly setup in the server; even with DTC configured, overhead of DTC would introduce big performance issues.
The reason is that each TableAdapter maintains its own database connection, and multiple operations with the same TableAdapter would lead to multiple database connections. A lightweight transaction is used in TransactionScope by default with SQL Server 2005, but DTC is promoted if multiple connections exist inside the same TransactionScope, which is the case in the above example.
We all know the cost of DTC is too expensive. Thus it's recommended to avoid using TableAdapters with TransactionScope, or avoid letting TableAdapters to manage the connections. You should manually and explicitly maintain all connections that are involved in a transaction scope if you have to use them together.
I have just read an article Managing Transactions using TransactionScope that provides an example of using TableAdapter and TransactionScope together:
public static int AddDepartmentWithEmployees(Department dept)The code demos using TableAdapter and TransactionScope to insert a Department record and a collection of Employee records in that department into database, presuming SQL Server 2005 in this case to take advantage of lightweight transaction.
{
int res = 0;
DepartmentAdapter deptAdapter = new DepartmentAdapter();
EmployeeAdapter empAdapter = new EmployeeAdapter();
using (TransactionScope txScope = new TransactionScope())
{
res += deptAdapter.Insert(dept.DepartmentName);
//Custom method made to return Department ID after inserting the department "Identity Column"
dept.DepartmentID = deptAdapter.GetInsertReturnValue();
foreach(Employee emp in dept.Employees)
{
emp.EmployeeDeptID = dept.DepartmentID;
res += empAdapter.Insert(emp.EmployeeName, emp.EmployeeDeptID);
}
txScope.Complete();
}
return res;
}
Looks really great and simple. But there's an issue of such implementation: Distributed Transaction Coordinator (DTC) is promoted at run time to complete this transaction. A run time error page will show up if DTC is not properly setup in the server; even with DTC configured, overhead of DTC would introduce big performance issues.
The reason is that each TableAdapter maintains its own database connection, and multiple operations with the same TableAdapter would lead to multiple database connections. A lightweight transaction is used in TransactionScope by default with SQL Server 2005, but DTC is promoted if multiple connections exist inside the same TransactionScope, which is the case in the above example.
We all know the cost of DTC is too expensive. Thus it's recommended to avoid using TableAdapters with TransactionScope, or avoid letting TableAdapters to manage the connections. You should manually and explicitly maintain all connections that are involved in a transaction scope if you have to use them together.
Wednesday, October 04, 2006
.NET Object Construction Sequence
How's a .NET object constructed and what's the order of initialization of object fields & static fields? Let's do a simple test:
Result:
class Program
{
static void Main()
{
new SubClass();
Console.Read();
}
class BaseClass
{
Logging baseField = new Logging("BaseClass field initializer");
static Logging baseStaticField = new Logging("BaseClass static field initializer");
static BaseClass()
{
Logging.Write("BaseClass static constructor");
}
public BaseClass()
{
Logging.Write("BaseClass constructor");
}
}
class SubClass : BaseClass
{
Logging subClassield = new Logging("SubClass field initializer");
static Logging subClassStaticField = new Logging("SubClass static field initializer");
static SubClass()
{
Logging.Write("SubClass static constructor");
}
public SubClass()
{
Logging.Write("SubClass constructor");
}
}
class Logging
{
static int count = 1;
public Logging(string info)
{
Write(info);
}
public static void Write(string info)
{
Console.WriteLine("{0}: {1}", count++, info);
}
}
}
The result shows a few interesting things:
1: SubClass static field initializer
2: SubClass static constructor
3: SubClass field initializer
4: BaseClass static field initializer
5: BaseClass static constructor
6: BaseClass field initializer
7: BaseClass constructor
8: SubClass constructor
1. Derived class field initializer first and base class field initializer next.
2. Class field initializer first and class constructor next.
3. Base class constructor first and derived class constructor next.
4. Static field initializer first and static constructor next.
5. Static constructor first and class constructor next.
5. Derived class static constructor first and base class static constructor next.
What's the reason for such order? Class constructor would reference those fields or static fields thus those fields must be initialized before the class constructor; same as static fields need to be initialized before the static constructor; on the other hand, base class constructor runs before derived class constructor because subclass construction may depend on the the state initialized by the base class, and base class usually has no knowledge about the subclass.
A common mistake is that a class field inialializer is using another non-static field, property or method. For example following code snippet will get compilation error:
The easy way to resolve above problem would be making the static GetTotalCount method. You can also initialize the field inside the class constructor if you don't like the static approach.
public int GetTotalCount()
{
return Service.GetTotalCount();
}
// Compile error: "A field initializer cannot reference the non-static field, method, or property..."
private int _totalNum = GetTotalCount();
// Compile error: "An object reference is required for the non-static field, method, or property..."
static int _totalNum = GetTotalCount();
Subscribe to:
Posts (Atom)