5. Ensure That Generic Classes Support Disposable Type Parameters
6. Use Delegates to Define Method Constraints on Generic Type Parameters
7. Do Not Create Generic Specialization on Base Classes or Interfaces
8. Prefer Generic Methods unless Type Parameters are Instance Fields
10.
Implement Class Interfaces in Addition to Generic Interfaces
When you use System.Object as a
parameter or return type, you have the potential to substitute the wrong type
which can cause run time errors. Also the use of
System.Object means that value types will have to boxed and unboxed every
time you coerce between the value type and
System.Object. This may have a significant impact on performance (applies
only with value types) The use of generics enforces correctness and improves
performance.
20 class Item1
21 {
22 /// <summary>
23 /// With generics, you can create simple functions to visit every item
24 /// in a collection
25 /// </summary>
26 public void TestGenerics1()
27 {
28 List<int> lst = new List<int>() { 1, 2, 3, 4 };
29 Action<int> delPrint = Print;
30 EnumerateAll(lst, delPrint);
31
32 // could also call EnumerateAll using the following syntax
33 EnumerateAll(lst, Print);
34 }
35 private void EnumerateAll<T>(IEnumerable<T> collection, Action<T> action)
36 {
37 foreach (T obj in collection)
38 action(obj);
39 }
40
41 private void Print<T>(T obj)
42 {
43 Trace.WriteLine("Item is: ", obj.ToString());
44 }
45
46 /// <summary>
47 /// To sort an array of objects that does not implement IComparable<T>,
48 /// you can supply a delegate that matches System.Comparison<T> delegate
49 /// </summary>
50 public void TestGenerics2()
51 {
52 List<string> lst = new List<string> { "B2", "B1", "A2", "A1" };
53 lst.Sort(MyStringComparison);
54 Trace.WriteLine(lst.ToString());
55 }
56
57 private int MyStringComparison<T>(T x, T y)
58 {
59 string s1 = x as string;
60 string s2 = y as string;
61
62 if ((x == null) || (y == null)) return -1;
63
64 if (s1[0] < s2[0])
65 return -1;
66 if (s1[0] > s2[0])
67 return 1;
68 else
69 {
70 if (s1[1] < s2[1])
71 return -1;
72 if (s1[1] > s2[1])
73 return 1;
74 else
75 return 0;
76 }
77 }
78
79
80 /// <summary>
81 /// System.Convertr<T,S> generic delegate converts one input object into a
82 /// corresponding output:
83 /// public delegate TOutput Converter<TInput, TOutput>(TInput input)
84 ///
85 /// Converter is therefore a delegate that takes an input of type TInput
86 /// and returns an object of type TOutput
87 /// </summary>
88 public void TestGenerics3()
89 {
90 // Create an array of PointF points (note use of C# 3.0 Object Initializers)
91 PointF[] ptsFloat = new PointF[] { new PointF{X=1.1F, Y=1.1F},
92 new PointF{X=2.1F, Y=2.1F},
93 new PointF{X=3.1F, Y=3.1F}};
94
95 // Now convert to a list of Point points
96 IEnumerable<Point> ptsInt = Transform<PointF, Point>(ptsFloat, PointFToPoint);
97
98 }
99
100 private Point PointFToPoint(PointF srcPoint)
101 {
102 return new Point((int)srcPoint.X, (int)srcPoint.Y);
103 }
104
105 /// <summary>
106 /// Transform has two type parameters: TInput and TOutput, which represent the input
107 /// and output types for the transform
108 /// </summary>
109 private IEnumerable<TOutput> Transform<TInput, TOutput>(IEnumerable<TInput> collSrc, Converter<TInput, TOutput> converter)
110 {
111
112 List<TOutput> lst = new List<TOutput>();
113 foreach (TInput item in collSrc)
114 lst.Add(converter(item));
115
116 return lst;
117 }
118
119 /// <summary>
120 /// In .NET 1.1, you had to create a class derived from EventArgs, then create
121 /// the delegate definition to refer to the derived class, and then create an event
122 /// definition to match the delegage:
123 /// public MyEventArgs : EventArgs { ... }
124 /// public delegate void MyEventHandler(object sender, MyEventArgs args);
125 /// public event MyEventHandler MyEvent
126 ///
127 /// .NET 2.0 now includes a generic definition for the event handler:
128 /// public delegate void EventHandler<TEventArgs>(Object sender, TEventArgs e) where TEventArgs : EventArgs
129 ///
130 /// Now declaring MyEvent looks like:
131 /// public event EventHandler<MyEventArgs> MyEvent;
132 /// </summary>
133 public void TestGenerics4()
134 {
135 this.MyEvent += new EventHandler<MyEventArgs>(Item1_MyEvent);
136
137 MyEvent(this, new MyEventArgs("hello from generic event handler"));
138 }
139
140
141 public event EventHandler<MyEventArgs> MyEvent;
142 public class MyEventArgs : EventArgs
143 {
144 private string msg;
145
146 public MyEventArgs(string messageData)
147 {
148 msg = messageData;
149 }
150 public string Message
151 {
152 get { return msg; }
153 set { msg = value; }
154 }
155 }
156
157 void Item1_MyEvent(object sender, Item1.MyEventArgs e)
158 {
159 Trace.WriteLine(e.Message);
160 }
161 }
First recall some generics terminology. Given Stack<T> and Stack<int>, the following terminology is used
Stack<T>: Generic type.
T : Generic type parameter
int : Type argument.
The whole point of creating a generic type is to create a type definition that can be used efficiently in as many scenarios as possible. You need to balance the safety of specifying 'constraints' against the extra work required by programmers to deal with every extra constraint. Strive for the minimal set of assumptions, but specify all assumptions as constraints.
If you do not specify any constraints, you must perform more checks at runtime. Constraints enable the compiler to expect capabilities in a type parameter T beyond those in the public interface in System.Object. Without any guidance from the developer, the compiler only assumes that the generic type exposed only the methods of System.Object. The compiler will emit errors on anything not defined in System.Object. This even includes fundamental operations such as new T() which is hidden if you define a constructor that has parameters. Therefore, use constraints to communicate to the compiler and users any assumptions you've made about the generic type.
Although you need to specify the necessary constraints, you also need to minimize the number of constraints you place on your generic type parameters. One of the most common ways is to ensure that your generic types (for example, Stack<T>) do not require functionalities that they do not use:
class Item2
{
/// <summary>
/// The alternative to not using contraints is to perform lots of casting and runtime
/// checks. AreEqual<T> is a generic method that tests if two objects are equal using
/// IComparable<T>. Becasue AreEqual<T> does not declare any contraints on T, it must
/// therefore check for the presence of IComparable<T>
/// Not specifying necessary constraints means that your methods/classes could easily
/// be misused, producing exceptions or runtime errors when client programmers guess
/// wrong
/// </summary>
public bool AreEqual<T>(T lhs, T rhs) //lhs, rhs: left-hand side and right-hand side
{
// If either parameter is null, return false
if ((lhs == null) || (rhs == null)) return false;
// Check that lhs implements IComparable<T>
IComparable<T> ILhs = lhs as IComparable<T>;
if (ILhs != null)
return (ILhs.CompareTo(rhs) == 0);
else
throw new InvalidOperationException("lhs does not implement IComparable<T>");
}
/// <summary>
/// The equivalent method is much simpler if you specify that T must implement IComparable<T>
/// This method trades run time errors (see exception code in AreEqual) with compile-time
/// errors: compiler generates errors if lhs or rhs does not derive from IComparable<T>
///
/// </summary>
public bool AreEqualWithConstraints<T>(T lhs, T rhs) where T : IComparable<T>
{
// No need to perform runtime checks
return (lhs.CompareTo(rhs) == 0);
}
/// <summary>
/// If AreEqual2<T> is defined in a generic type (say, Stack<T>) that declares the
/// IEquatable<T> constraint, it will call IEquatable<T>.Equals. Otherwise, the C#
/// compiler cannot assume that IEquatable<T> is present and will instead call
/// System.Object.Equals
///
/// This example illustrates the basic difference between C# generic and C++ Templates
/// In C#, the compiler must generate IL using only the information specified in constraints.
/// Even if the type specified for a specific instantiation has a better method (ie,
/// IEquatable<T>.Equals), it will not be used unless it was specified when the generic type
/// (Stack<T>) was compiled.
///
/// So, must every using AreEqual2<T> implement IEquatable<T>? The answer is no. Rather than
/// adding a IEquatable<T> constraint on AreEqual2<T>, in the function body check for existence
/// of IEquatable<T>, and if not present, transparently downgrade to the less preferred
/// Object.Equals method. See AreEqual3<T>
/// </summary>
public bool AreEqual2<T>(T lhs, T rhs)
{
return lhs.Equals(rhs);
}
/// <summary>
///
/// </summary>
public bool AreEqual3<T>(T lhs, T rhs)
{
// If either parameter is null, return false
if ((lhs == null) || (rhs == null)) return false;
// Check that lhs implements IComparable<T>
IEquatable<T> ILhs = lhs as IEquatable<T>;
if (ILhs != null)
return (ILhs.Equals(rhs));
else
return lhs.Equals( rhs );
}
/// <summary>
/// Recall that the new() constraint indicates to the compiler that a generic type parameter
/// (T in Stack<T>) supports a public default constructor (constructor with no params). In some
/// cases you can replace the new() constraint by replacing 'new' calls with default(), which is
/// an operator that initialzes a variable to its default value (0s for value types and null for
/// reference types).
///
/// The following method wraps a factory method (FactoryFunc delegate) to create an object
/// of type T. If the factory method fails to create an object of type T, it returns the value
/// returned by the default constructor. This method must therefore specify the new() constraint.
/// Also because of the test for null, the behavior is different for value types. Value types
/// cannot be null and therefore the caluse under the if statement will never be executed (the
/// JIT compiler will remove the null test if T is a value type). Therefore, be careful when
/// adding new() constraint as it creates assumptions about how an object will be constructed.
/// </summary>
public delegate T FactoryFunc<T>();
public T Factory<T>( FactoryFunc<T> MakeNewT) where T : new ()
{
T val = MakeNewT();
if (val == null)
return new T(); // replace with default(T) and remove the 'new()' constraint
else
return val;
}
}
// Test class that implements IComparable<T>
class Order : IComparable<Order>
{
public int nOrderID;
public Order(int id) { nOrderID = id; }
#region IComparable<Order> Members
public int CompareTo(Order other)
{
if ((this == null) && (other == null)) return 0;
if ((this == null) && (other != null)) return -1;
if ((this != null) && (other == null)) return 1;
return nOrderID.CompareTo(other.nOrderID);
}
#endregion
}
// Test class that does not implement IComparable<T>
class Customer
{
public int nCustomerID;
public Customer(int id) { nCustomerID = id; }
}
Given Stack<T>, you can supply different generic type parameter T to create different types but with similar functionality (Stack<int>, Stack<Order>, Stack<Student>, etc.) However, generics may limit you by not being able to take advantage of a more specific superior algorithm. This is addressed by recognizing that the algorithm can be more efficient when a generic type parameter T has specific capabilities, i.e., implements a specific interface, and then utilize that capability to implement a more efficient and superior algorithm.
class Item3
{
public void TestReverseEnumerable()
{
List<int> lst = new List<int>();
lst.Add(1);
lst.Add(2);
lst.Add(3);
ReverseEnumerable<int> re = new ReverseEnumerable<int>(lst);
foreach (int n in re)
Trace.WriteLine( n );
}
}
/// <summary>
/// Helper class
/// Recall that IEnumerable<T> exposes an enumerator which supports a simple iteration
/// over a collection of type T
///
/// ReverseEnumerable constructor takes an input parameter that supports IEnumerable<T>.
/// IEnumerable<T> does not support random access
/// </summary>
class ReverseEnumerable<T> : IEnumerable<T>
{
private IList<T> originalSequence;
IEnumerable<T> sourceSequence;
// Constructor
public ReverseEnumerable(IEnumerable<T> sequence)
{
sourceSequence = sequence;
// Make use of the fact that many types that implement IEnumerable<T> also implement
// IList<T>. This improves the efficiency of the code by not having to create an unnecessary
// copy of sequence. IList<T> enables a more efficient algorithm than does IEnumerable<T>
// and we have not forced consumers of this class to provide more functionality
// If 'sequence' does not implement IList<T>, 'originalSequence' is null, and this works
// fine as it gets initialized in GetEnumerator() function.
originalSequence = sequence as IList<T>;
}
#region IEnumerable<T> Members
public IEnumerator<T> GetEnumerator()
{
// Create a copy of the source sequence so it can be reversed
if (originalSequence == null)
originalSequence = new List<T>(sourceSequence);
// Return an object that implements IEnumerator<T>
return new ReverseEnumerator<T>(originalSequence);
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
}
/// <summary>
/// Helper class
/// Recall that IEnumerator<T> supports a simple iteration over a collection of type T
/// </summary>
class ReverseEnumerator<T> : IEnumerator<T>
{
private int nCurrentIndex;
private IList<T> collection;
// Constructor
public ReverseEnumerator(IList<T> source)
{
collection = source;
nCurrentIndex = source.Count; // nCurrenIndex starts from the end
}
#region IEnumerator<T> Members
public T Current
{
get { return collection[nCurrentIndex]; }
}
#endregion
#region IDisposable Members
public void Dispose()
{
// No implementation is needed as there are no unmanaged resources
}
#endregion
#region IEnumerator Members
object System.Collections.IEnumerator.Current
{
get { return this.Current; }
}
public bool MoveNext()
{
return (--nCurrentIndex >= 0);
}
public void Reset()
{
nCurrentIndex = collection.Count;
}
#endregion
}
Generics allow you to minimize the amount of code required to
create and support a given pattern. For example, consider the
List<T>.Find( Predicate<T> match) function that
finds the first element that meets a specific condition. The
List class also contains similar methods for
FindAll, TrueForAll,
ForEach, and others. The collection class now
contains a common algorithm to enumerate all nodes in a collection; the
collection class provides a generic method to visit each element and the user
need only supply the logic that is to be applied to each element in the
collection. You examine all the elements for a variety of reasons: searching for
something, testing the values of the elements, or transforming the values
of the elements.
In the same way, you can implement many design patterns by defining the proper
delegates or events in the form of predicates. These generic implementations can
be reused whenever you need to implement the same pattern. In other words, you
may find that you create algorithms that use the type of an object to implement
the algorithm. In that case, you can often create a single generic version of
the algorithm by abstracting away the type parameters into generic parameters.
Then the compiler can create the specific versions for you
/// <summary>
/// This is a helper class that illustrates a pattern for serialization/deserialization
/// This is a non-generic version that can serialize any arbitrary type
///
/// XmlPersistenceManager works but its use requires a type be specified as it (the type)
/// cannot be inferred. There is also the issue of type safety: every time you call
/// LoadFromFile() you must cast or convert the returned object. There is also a hidden
/// inefficiency: every call to one of the class methods create a new XmlSerializer which
/// can indirectly create lots of temporary object that are not needed.
/// </summary>
internal static class XmlPersistenceManager
{
private static object LoadFromFile(Type typeToLoad, string strFilePath)
{
// If file does not exist, return the defautl value for object (i.e., null)
if (!File.Exists(strFilePath))
return default(object);
// Otherwise, load stream from file and convert to an object
using (TextReader tr = new StreamReader(strFilePath))
{
XmlSerializer serializer = new XmlSerializer(typeToLoad);
return serializer.Deserialize(tr);
}
}
private static void SaveToFile(string strFilePath, object ob)
{
Type typeToSave = ob.GetType();
using (TextWriter tw = new StreamWriter(strFilePath))
{
XmlSerializer serializer = new XmlSerializer(typeToSave);
serializer.Serialize(tw, ob);
}
}
}
/// <summary>
/// The following is a generic version of XmlPersistenceManager that is type-safe
/// and efficient
/// </summary>
internal static class GenericXmlPersistenceManager<T>
{
// Cache the serializer once it is created
private static XmlSerializer serializer = null;
private static T LoadFromFile(string strFilePath)
{
// Create the XmlSerializer once, and cache it for the rest of time as each
// XmlSerializer is tied to a specific Type class
if (serializer == null)
serializer = new XmlSerializer(typeof(T));
// If file does not exist, return the defautl value for object (i.e., null)
if (!File.Exists(strFilePath))
return default(T);
// Otherwise, load stream from file and convert to an object
using (TextReader tr = new StreamReader(strFilePath))
{
return (T)serializer.Deserialize(tr);
}
}
private static void SaveToFile(string strFilePath, T ob)
{
// Create the XmlSerializer once, and cache it for the rest of time as each
// XmlSerializer is tied to a specific Type class
if (serializer == null)
serializer = new XmlSerializer(typeof(T));
using (TextWriter tw = new StreamWriter(strFilePath, false))
{
serializer.Serialize(tw, ob);
}
}
}
Constraints on generic classes imply two things:
Run-time errors are converted to compile-time errors.
Specify requirements of the generic type parameter (T in Stack<T>)
In most cases, you don't care what capabilities a generic type parameter T has beyond those your generic type (Stack<T>) expects and uses. In the special case of a generic type parameter that implements IDisposable, you need to be aware of resource leaks. The recommended approaches are:
Wrap local instances of type parameters in a using statement. See OrderEngine_v1<T>.ProcessOrder_NoLeak() method.
Member variable-instances of type parameters should be managed externally; let the caller create and manage calling Dipose on each generic type parameter.
Therefore, if you create instances of any of the types described by your generic class's type parameters, you must consider that those types may implement IDisposable. You must code defensively and ensure that you don't leak resources when those objects go out of scope. Sometimes you can do that by refactoring the code so that it does not create those instances. At other times the best design is to create and use local variables, writing the code to dispose of them if needed.
public class Item5
{
internal void TestLeakableGenericClass()
{
// Note that 'OrderEngine_v1' is responsible for creating an instance
// of BookORder
OrderEngine_v1<BookOrder> v1 = new OrderEngine_v1<BookOrder>();
v1.ProcessOrder_CanCauseLeak("test");
v1.ProcessOrder_NoLeak("test");
// Note that we are now responsible for creating and managing BookOrder
// object
BookOrder bo = new BookOrder();
using (bo as IDisposable)
{
OrderEngine_v2<BookOrder> v2 = new OrderEngine_v2<BookOrder>(bo);
v2.ProcessOrder("test");
} // bo.Dispose() called her, IF bo implements IDisposable
}
}
/// <summary>
/// Helper class to illustrate possible resource leaks when using generic type parameters
/// In method ProcessOrder_CanCauseLeak, we create and use an instance of the generic
/// type parameter (T). But what if T was a disposable type that required Dispose to be
/// explicitly called? You will introduce a resource leak if T implements IDisposable
/// </summary>
public class OrderEngine_v1<T> where T : IOrder, new()
{
/// <summary>
/// Create an instance of T. What if T required Dispose to be called on it? You get
/// a resource leak
/// </summary>
public void ProcessOrder_CanCauseLeak(string name)
{
// Create an instance of T. Note that we own and control the instance
T order = new T();
order.CreateOrder(name);
}
/// <summary>
/// To prevent the possibility of a resouce leak as in ProcessOrder_CanCauseLeak, in every
/// case where you create a local variable of type T, you need to check whether T implements
/// IDisposable, and, if so, dispose of it correctly
/// </summary>
public void ProcessOrder_NoLeak(string name)
{
// Create an instance of T. Note that we own and control the instance
T order = new T();
// Note that casting syntax inside using. The compiler creates a hidden local variable that
// stores a reference to the order cast as an IDisposable. If T does not implement IDisposable,
// then the value of this local variable is null. In those cases, the compiler does not call
// Dispose(), because it checks against null before doing this extra work. However, in all cases
// where T implements IDisposable, the compiler generates a call to the Dispose() method upon
// exiting the using block.
using (order as IDisposable)
{
order.CreateOrder(name);
}
}
}
/// <summary>
/// In OrderEngine_v1, an instance of T was created as a local variable inside a function.
/// Things can get more complicated when the generic class needs to create and use an instance
/// of T as a member variable (see _order member variable below).
///
/// The recommended approach is to change the interface of the generic type so as to let the
/// user of the class be responsible for
/// 1. Creating an instance of the generic type parameter (T), and
/// 2. Calling Dispose on the generic-type-parameter instance
/// </summary>
public class OrderEngine_v2<T> where T : IOrder
{
T _order = default(T);
// Constructor
public OrderEngine_v2(T order)
{
// We do not create the order. The user of the class create 'order' and passes it in
// Contrast this to OrderEngine_v1 methods
_order = order;
}
public void ProcessOrder(string name)
{
// No need to check for IDisposable. This responsibility has been moved to the user.
_order.CreateOrder(name);
}
}
public interface IOrder
{
void CreateOrder(string strName);
}
// BookOrder is a type argument for OrderEngine_v1<T>
public class BookOrder : IOrder
{
public string _strBookName;
#region IOrder Members
public void CreateOrder(string strName)
{
_strBookName = strName;
// Other required steps ...
}
#endregion
}
// DVDOrder is a type argument for OrderEngine_v1<T>
public class DVDOrder : IOrder
{
public string _strDVDName;
#region IOrder Members
public void CreateOrder(string strName)
{
_strDVDName = strName;
// Other required steps ...
}
#endregion
}
Assume you have a generic class that needed an Add() method on T. You would create an IAdd<T> interface and you'd code against that interface. Every developer who wants to use your generic class would need to do more work: They'd need to create a class that implements IAdd<T>, define the methods needed for IAdd<T>, and then specify the closed generic class for your generic class definition. See DoNotUseCode() and DoNotUse namespaces in the code below. This is solved by specifying a delegate signature that matches the method your generic class needs to call. See UseThisCode and UseThis namespace.
Therefore, when it's unwieldy to use an interface to define a constraint (see DoNotUse namespace), you can define a method signature and a delegate type that suits your needs (see UseThis namespace). Then you add an instance of that delegate to the list of the parameters of the generic method (Add method). The developers using your class can use a lambda expression to define that method, writing much less code, in a much clearer fashion. There's no extra code to support the syntax of interface-based constraints.
class Item6
{
/// <summary>
/// Incorrect approach to use an Add() method on T in a generic class
/// </summary>
public void DoNotUseCode()
{
DoNotUse.OrderEngine<DoNotUse.BookOrder> oe = new DoNotUse.OrderEngine<DoNotUse.BookOrder>();
DoNotUse.BookOrder bo1 = new Samples.DoNotUse.BookOrder(10);
DoNotUse.BookOrder bo2 = new Samples.DoNotUse.BookOrder(20);
DoNotUse.BookOrder boSum = oe.AddOrder(bo1, bo2);
}
/// <summary>
/// Correct approach to use an Add() method on T in a generic class.
/// </summary>
public void UseThisCode()
{
UseThis.OrderEngine<UseThis.BookOrder> oe = new UseThis.OrderEngine<UseThis.BookOrder>();
UseThis.BookOrder bo1 = new UseThis.BookOrder(1);
UseThis.BookOrder bo2 = new UseThis.BookOrder(2);
UseThis.BookOrder sum = oe.AddOrder(bo1, bo2, delegate(UseThis.BookOrder o1, UseThis.BookOrder o2)
{
int nTotal = o1.nTotal + o2.nTotal;
UseThis.BookOrder s = new Samples.UseThis.BookOrder(nTotal);
return s;
});
}
/// <summary>
/// This method illustrates the use of delegate-based contracts to create algorithms that
/// operate on sequences. In this example, Merge helper method enumerates both input sequences,
/// and for each pair of items in the input sequence, it calls the merger delegate to return a
/// new constructed TOutput object
///
/// </summary>
public void GenericMerge()
{
float[] dXValues = { 2, 4, 6, 8, 10 };
float[] dYValues = { 1, 3, 5, 7, 9 };
IEnumerable<PointF> Points = Merge(dXValues, dYValues, delegate(float x, float y)
{
return new PointF(x, y);
});
}
private IEnumerable<TOutput> Merge<T1, T2, TOutput>(IEnumerable<T1> XValues, IEnumerable<T2> YValues, Func<T1, T2, TOutput> merger)
{
// Get enumerators to help traverse Xvalues and YValues arrays
IEnumerator<T1> XEnumerator = XValues.GetEnumerator();
IEnumerator<T2> YEnumerator = YValues.GetEnumerator();
// Iterator over both X and Y enumerators
while (XEnumerator.MoveNext() && YEnumerator.MoveNext())
{
yield return merger(XEnumerator.Current, YEnumerator.Current);
}
}
}
namespace DoNotUse
{
// Created by class developer
interface IAdd<T>
{
T Add(T other);
}
// Created by class developer
class OrderEngine<T> where T : IAdd<T>
{
public T AddOrder(T order1, T order2)
{
T orderSum = order1.Add(order2);
return orderSum;
}
}
// Created by developer wishing to use OrderEngine<T>
class BookOrder : IAdd<BookOrder>
{
public int nTotal = 0;
public BookOrder(int tot) { nTotal = tot; }
public BookOrder Add(BookOrder other)
{
int nSum = this.nTotal + other.nTotal;
BookOrder sum = new BookOrder(nSum);
return sum;
}
}
}
namespace UseThis
{
class OrderEngine<T>
{
public T AddOrder(T order1, T order2, Func<T, T, T> AddFunc)
{
T sum = AddFunc(order1, order2);
return sum;
}
}
class BookOrder
{
public int nTotal = 0;
public BookOrder(int tot) { nTotal = tot; }
}
}
When you create generic classes or methods, you are responsible for creating a set of methods that will enable developers using that class to safely use your code with minimal confusion. This means that you must pay careful attention to overload resolution. Always remember that generic methods are always perfect matches, so they win over base class methods.
class Item7
{
/// <summary>
/// The output of the first WriteMessage(ob) shows an important concept:
/// WriteMessage<T>(T obj) is a better match than WriteMessage(MyBase b) for an object that is
/// derived from MyBase. That's because the compiler can make an exact match by substituting
/// MyDerived for T in that message, and WriteMessage(MyBase) requires an implicit conversion.
/// </summary>
public void Test()
{
Item7NS.MyDerived ob = new Item7NS.MyDerived();
// Generic methods are always perfect matches, so they win over base class methods.
WriteMessage(ob); // WriteMessage<T>(T ob)
// The following two statements show how to control which methods get invoked
WriteMessage((Item7NS.IMyInterface)ob); // WriteMessage(IMyInterface ob)
WriteMessage((Item7NS.MyBase)ob); // WriteMessage(MyBase ob)
Item7NS.MyOtherDerived ob2 = new Item7NS.MyOtherDerived();
WriteMessage(ob2); // WriteMessage<T>(T ob)
WriteMessage((Item7NS.IMyInterface)ob2); // WriteMessage(IMyInterface ob)
}
private void WriteMessage(Item7NS.MyBase ob)
{
Trace.WriteLine("WriteMessage(MyBase ob)");
}
private void WriteMessage(Item7NS.IMyInterface ob)
{
Trace.WriteLine("WriteMessage(IMyInterface ob)");
}
private void WriteMessage<T>(T ob)
{
Trace.WriteLine("WriteMessage<T>(T ob)");
}
}
namespace Item7NS
{
class MyBase { /* Empty implementation */ }
interface IMyInterface
{
void DoSomething();
}
class MyDerived : MyBase, IMyInterface
{
#region IMyInterface Members
public void DoSomething()
{
Trace.WriteLine("MyDerived.IMyInterface.DoSomething");
}
#endregion
}
class MyOtherDerived : IMyInterface
{
#region IMyInterface Members
public void DoSomething()
{
Trace.WriteLine("MyDerived.IMyInterface.DoSomething");
}
#endregion
}
A utility class that contains generic methods can specify different
constraints for each method. Those different constraints can make it much easier
for the compiler to find the best match and therefore much easier for your
clients to use your algorithms. Also, each generic type parameter (T in
Stack<T>) need satisfy the constraints only for the methods in which it is used.
With generic classes, in contrast, the generic type parameters must satisfy all
the constraints defined for the complete class. As you expand a class over time,
it becomes much more constraining if the generic type parameters are specified
on the class level rather than at the method level.
Obviously, not every generic algorithm is suited for generic methods instead of
a generic class. Some simple guidelines can help you determine which to use. In
two cases you must make a generic class: The first occurs when your class
stores a value of one of the generic type parameters as part of its internal
state. (Collections are an obvious example.) The second occurs when your class
implements a generic interface. Except for those two cases, you can usually
create a non-generic class and use generic methods. You'll end up with more
granularity in your options for updating the algorithms in the future.
class Item8
{
public void Test()
{
// Utilities is a generic class. Every call to methods on Utilities<> must
// specify the generic type parameter T
double dMax = Item8NS.Utilities<double>.Max(1.0, 1.2);
int nMax = Item8NS.Utilities<int>.Max(3, 4);
// Utilities2 is no longer a generic class. It is a non-generic class with
// generic methods. If there is a specific version of the parameter type,
// the compiler calls that version. If there isn't a specific version, the
// compiler calls the generic version
double dMax2 = Item8NS.Utilities2.Max(1.0, 1.01);
int nMax2 = Item8NS.Utilities2.Max<int>(1, 2);
}
}
namespace Item8NS
{
/// <summary>
/// A generic utility class rather than generic methods. Every call to methods
/// of this class must specify the generic type parameter.
///
/// Many of the built-in types already have accessible Max and Min methods defined.
/// Math.Max() and Math.Min() are defined for all the numeric types. Instead of using
/// those, your generic class always picks up the version you've created using Comparer<T>.
/// That works, but it forces extra runtime checks to determine whether a type implements
/// IComparer<T>, followed by a call to the correct method
/// </summary>
/// <typeparam name="T"></typeparam>
public static class Utilities<T>
{
public static T Max(T lhs, T rhs)
{
bool bLhsBigger = (Comparer<T>.Default.Compare(lhs, rhs) < 0);
return (bLhsBigger) ? lhs : rhs;
}
public static T Min(T lhs, T rhs)
{
bool bLhsBigger = (Comparer<T>.Default.Compare(lhs, rhs) < 0);
return (bLhsBigger) ? lhs : rhs;
}
}
/// <summary>
/// Utilities2 is no longer a generic class. It is a non-generic class with generic
/// methods. If there is a specific version of the parameter type, the compiler calls
/// that version. If there isn't a specific version, the compiler calls the generic
/// version.
///
/// Whenever you use a new type Max<T> or Min<T> , a new version of Max<T> or Min<T> is
/// generated by the compiler. If you had instead applied the type parameter to the class
/// declaration as in Utilities<T>, every Min or Max would be forced to hold only one type.
/// Either approach is valid, but the semantics are very different.
/// </summary>
public static class Utilities2
{
// Generic methods
public static T Max<T>(T lhs, T rhs)
{
bool bLhsBigger = (Comparer<T>.Default.Compare(lhs, rhs) < 0);
return (bLhsBigger) ? lhs : rhs;
}
public static T Min<T>(T lhs, T rhs)
{
bool bLhsBigger = (Comparer<T>.Default.Compare(lhs, rhs) < 0);
return (bLhsBigger) ? lhs : rhs;
}
// Overloadsc
public static double Max(double lhs, double rhs)
{
return Math.Max(lhs, rhs);
}
}
}
A common problem for many developers is how to create a method signature for methods that logically return more than one item. Many developers turn to ref or out parameters in those cases. But it's better to define generic tuples that can return multiple discrete values. A tuple is nothing more than a composite with n elements.
By creating and using tuple classes, you avoid the need to create ref and out parameters for multiple logical methods. That will make it easier to compose method calls that work with these tuples.
In general, methods that take out and ref parameters do not support composition very well. Methods returning a single value (however complex that value might be) compose better.
class Item9
{
public void Test()
{
Item9NS.Tuple<string, long> details = GetNameAndID();
}
private Item9NS.Tuple<string, long> GetNameAndID()
{
return new Item9NS.Tuple<string, long>("Yazan", 123);
}
}
namespace Item9NS
{
/// <summary>
/// A tuple is just a composite type that n elements. Note how IEquatable<T>
/// is defined: IEquatable<Tuple<T1, T2>> and IEquatable<T>.
///
/// This tuple can be the return type for any method that has two logical return
/// values.
/// </summary>
public class Tuple<T1, T2> : IEquatable<Tuple<T1, T2>>
{
private readonly T1 first;
private readonly T2 second;
public Tuple(T1 t1, T2 t2)
{
first = t1;
second = t2;
}
public T1 First { get { return first;}}
public T2 Second {get {return second;}}
#region IEquatable<Tuple<T1,T2>> Members
public bool Equals(Tuple<T1, T2> other)
{
int nFirstCompare = Comparer<T1>.Default.Compare(this.first, other.first);
int nSecondCompare = Comparer<T2>.Default.Compare(this.second, other.second);
bool bEqual = (nFirstCompare == 0) && (nSecondCompare == 0);
return bEqual;
}
#endregion
}
}
Your classes will be much more useful if you support the classic non-generic interfaces in addition to the generic interfaces you'll want to support in new libraries. In Item10NS.Name class, All the core capabilities of the equality and ordering are implemented in terms of the generic versions. But a generic implementation does not play well with any code written using the .NET 1.x methods. What's more, you may need to integrate types from various systems that represent the same logical type. For example, you may have two systems that have the concept of Order; you need a cross-type equality relationship between these two orders.
Note on IComparable<T> and
IComparer<T>:
These interfaces support ordering comparisons, i.e., their methods are used to
indicate
if two objects sort the same. IComparable<T> is often implemented by types whose
values can
be ordered such as SortedList<K,V>.
IComparer<T> on the other hand, is not
implemented on a
type directly, but rather it is implemented on helper objects, typically to
provide multiple
sort orders
Note on Comparer<T> class:
This is an abstract base class for implementation of IComparer<T>. You typically
derive from
this class to provide a custom implementation of the IComparer<T> interface. In addition to
deriving from this class, Comparer<T>.Default property is often used to retrieve
a Comparer<T>
object that uses IComparable<T> interface.
For string comparisons, the StringComparer class is recommended over
Comparer<String>. Properties of
the StringComparer class return predefined instances that perform string
comparisons with different
combinations of culture-sensitivity and case-sensitivity.
namespace Item10NS
{
/// <summary>
/// In addition to implementing the generic IEquatable<T> and IComparable<T>, this class
/// also implements the non-generic IComparable. Notice that the non-generic IComparable
/// interface is defined using explicit interface implementation. This practice ensures
/// that no one accidentally gets the classic interface instead of the preferred generic
/// interface. In normal use, the compiler will choose the generic method over the explicit
/// interface method. Only when the called method has been typed to the classic interface
/// (IComparable) will the compiler generate a call to that interface member
///
/// </summary>
public class Name : IEquatable<Name>, IComparable<Name>, IComparable
{
private string _strFirst;
private string _strLast;
public string First
{
get { return _strFirst; }
set { _strFirst = value; }
}
public string Last
{
get { return _strLast; }
set { _strLast = value; }
}
#region IEquatable<Name> Members
public bool Equals(Name other)
{
// If null is passed, equality should be false
if (object.ReferenceEquals( other, null)) return false;
int nFirstComp = StringComparer.CurrentCulture.Compare(this.First, other.First);
int nSecondComp = StringComparer.CurrentCulture.Compare(this.Last, other.Last);
return ((nFirstComp == 0) && (nSecondComp == 0));
}
#endregion
#region IComparable<Name> Members
public int CompareTo(Name other)
{
// If null is passed in, indicate that this object is greater
if (Object.ReferenceEquals( null, other)) return 1;
int nFirstComp = StringComparer.CurrentCulture.Compare( this.First, other.First);
if (nFirstComp != 0) return nFirstComp;
int nSecondComp = StringComparer.CurrentCulture.Compare( this.Last, other.Last);
return nSecondComp;
}
#endregion
#region IComparable Members
int IComparable.CompareTo(object obj)
{
if (obj.GetType() != this.GetType())
throw new InvalidOperationException("obj is not a Name object");
return this.CompareTo(obj as Name);
}
#endregion
// Implementing IEquatable<T> means implementing operator== and operator!=
public static bool operator ==(Name lhs, Name rhs)
{
if (lhs == null) return (rhs == null);
return lhs.Equals(rhs);
}
public static bool operator !=(Name lhs, Name rhs)
{
if (lhs == null) return (rhs != null);
return !lhs.Equals(rhs);
}
// Implementing IComparable<T> implies there is an ordering relation. We should therefore implement
// operator <, >, <=, and >=
public static bool operator <(Name lhs, Name rhs)
{
if (lhs == null) return (rhs != null);
return (lhs.CompareTo(rhs) < 0);
}
public static bool operator >(Name lhs, Name rhs)
{
if (lhs == null) return false;
return (lhs.CompareTo(rhs) > 0);
}
public static bool operator <=(Name lhs, Name rhs)
{
if (lhs == null) return true;
return (lhs.CompareTo(rhs) <= 0);
}
public static bool operator >=(Name lhs, Name rhs)
{
if (lhs == null) return (rhs == null);
return (lhs.CompareTo(rhs) >= 0);
}
}
}
You can't know the optimum number of threads that should be created
for your application. However, the .NET thread pool has all the knowledge
necessary to optimize the number of active threads on the target system.
Furthermore, if you have created too many tasks and threads for the target
machine, the thread pool queues up additional requests until a new background
thread is available.
QueueUserWorkItem uses the thread pool to manage
resources for you. You queue up a worker item, and when a thread is available,
it executes your thread procedure. The thread pool's job is to
make sure that a thread becomes available quickly. Essentially, you fire the
request and forget it
All threads belonging to the thread pool used by QueueUserWorkItem are background threads. This means that you don't need to clean up those threads before your application exits. If your application exits while these background threads are running, the system stops those tasks and unloads everything related to your application. You need to ensure that you stop all non-background threads in your application before the system will unload your application.
On the other hand, because background threads are killed without
warning, you need to be careful how you access system resources to
ensure that application termination at the wrong time doesn't leave the system
in an unstable state. In many cases, when a thread is terminated, the runtime
raises a ThreadAbortException on that thread. When
an application terminates with background threads running, those background
threads receive no notification that the application is terminating. They are
simply stopped. If your threads may leave system resources in an unstable state,
you should not use background threads.
Two important factors result in the higher performance of the thread pool
compared with creating your own threads manually. First, the thread pool reuses
threads as they become available for work. When you manually create new threads,
you must create a new thread for each new task. The creation and destruction of
those threads take more time than the .NET thread pool management.
Second, the thread pool manages the active number of threads for you. If you
create too many threads, the system queues them up, and they wait to execute
until enough resources are available. QueueUserWorkItem
hands work to the next available thread in the thread pool and does some thread
resource management for you. If all the threads in the application's thread pool
are busy, it queues tasks to wait for the next available thread.
The BackgroundWorker class is built on top of ThreadPool and adds many features for inter-thread communication:
The single most important issue you must deal with is exceptions in your WaitCallback, the method that does the work in the background thread. If any exceptions are thrown from that method, the system will terminate your application. It doesn't simply terminate that one background thread; it terminates the entire application. With BackgroundWorker on the other hand catches all exceptions thrown in the thread function and propagates these exceptions to the RunWorkerCompletedEventArgs.Error property.
The thread function for a BackgroundWorker. QueueUserWorkItem doesn't have any built-in capability to handle reporting errors.
QueueUserWorkItem does not give you any built-in methods to communicate between the background threads and the foreground thread. It doesn't provide any built-in means for you to detect completion, track progress, pause tasks, or cancel tasks. When you need those capabilities, you must use BackgroundWorker component, which is built on top of the QueueUserWorkItem functionality. You don't have to design your own communication protocols between foreground and background threads.
The BackgroundWorker class performs its background tasks using ThreadPool and by using QueueUserWorkItem internally. BackgroundWorker uses events to communicate between the foreground and background threads.
BackgroundWorker for multiple background requests. You need to check the IsBusy property of BackgroundWorker to see whether BackgroundWorker is currently running a task. When you need to have multiple background tasks running, you can create multiple BackgroundWorker objects. Each will share the same thread pool, so you have multiple tasks running just as you would with QueueUserWorkItem. You need to make sure that your event handlers use the correct sender property. This practice ensures that the background threads and foreground threads are communicating correctly.
class Item12
{
private int numberToCompute = 0;
private int highestPercentageReached = 0;
public void TestBackgroundWorker()
{
// Create a BackgroundWorker object and set its events to control and monitor
// the the background thread
BackgroundWorker bw = new BackgroundWorker();
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.Disposed += new EventHandler(bw_Disposed);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
// These properties control how the foreground and background threads interact
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
// Setup the thread function and start it
numberToCompute = 100;
highestPercentageReached = 0;
bw.RunWorkerAsync(numberToCompute);
}
# region Event handlers for BackgroundWorkder in TestBackgroundWorker
/// <summary>
/// Background thread is finished when the DoEvent handler function exits.
/// </summary>
void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = sender as BackgroundWorker;
e.Result = ComputeFibonacci((int)e.Argument, bw, e);
} // RunWorkerCompleted event fired here.
/// <summary>
/// Note the RunWorkerCompletedEventArgs.Error property that contains
/// any exception information generated by the thread function.
/// </summary>
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
Trace.WriteLine("Operation cancelled");
else if (e.Error != null)
Trace.WriteLine("Error: " + e.Error.Message);
else
Trace.WriteLine("Result: " + e.Result.ToString());
}
void bw_Disposed(object sender, EventArgs e)
{
Trace.WriteLine("");
}
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
Trace.WriteLine("Progress: " + e.ProgressPercentage);
}
#endregion
// This is the method that does the actual work. For this
// example, it computes a Fibonacci number and
// reports progress as it does its work.
long ComputeFibonacci(int n, BackgroundWorker worker, DoWorkEventArgs e)
{
// The parameter n must be >= 0 and <= 91. Fib(n), with n > 91, overflows a long.
// Note that the exception will be reported in RunWorkerCompletedEventArgs.Error
// property
if ((n < 0) || (n > 91))
throw new ArgumentException("value must be >= 0 and <= 91", "n");
long result = 0;
// Abort the operation if the user has canceled.
// Note that a call to CancelAsync may have set
// CancellationPending to true just after the
// last invocation of this method exits, so this
// code will not have the opportunity to set the
// DoWorkEventArgs.Cancel flag to true. This means
// that RunWorkerCompletedEventArgs.Cancelled will
// not be set to true in your RunWorkerCompleted
// event handler. This is a race condition.
if (worker.CancellationPending)
{
e.Cancel = true;
}
else
{
if (n < 2)
{
result = 1;
}
else
{
result = ComputeFibonacci(n - 1, worker, e) +
ComputeFibonacci(n - 2, worker, e);
}
// Report progress as a percentage of the total task.
int percentComplete = (int)((float)n / (float)numberToCompute * 100);
if (percentComplete > highestPercentageReached)
{
highestPercentageReached = percentComplete;
worker.ReportProgress(percentComplete);
}
}
return result;
}
}
The lock statement generates exactly
the same code as if you used Monitor.Enter() and
Monitor.Exit() correctly. Furthermore, it's easier
and it automatically generates all the exception-safe code you
need. The lock statement automatically generates
exception-safe code. Also, it generates more-efficient code than
Monitor.Enter() and
Monitor.Exit(), because it needs to evaluate the target object only once.
So, by default, you should use the lock statement to
handle the synchronization needs.
class Item13
{
private object _obLock = new object();
private int _nTotal = 0;
// The following two methods are identical
public void IncrementTotalWithLock()
{
lock (_obLock)
{
_nTotal++;
}
}
public void IncrementTotalWithMonitor()
{
System.Threading.Monitor.Enter(_obLock);
try
{
_nTotal++;
}
finally
{
System.Threading.Monitor.Exit(_obLock);
}
}
/// <summary>
/// Monitor.Enter locks a box containing _nTotal while Monitor.Exit unlocks
/// a different box containing _nTotal
/// </summary>
public void IncrementTotal_Wrong1()
{
// Multiple threads call Monitor.Enter successfully because each thread is locking
// a different object (eacah call to Enter creates a different box for _nTotal)
System.Threading.Monitor.Enter(_nTotal);
try
{
_nTotal++;
}
finally
{
// SynchronizationLockException because Monitor.Exit is attempting to unlock
// an object that was no locked in the first case (call to Monitor.Exit creates
// yet another object containing _nTotal)
System.Threading.Monitor.Exit(_nTotal);
}
}
public void IncreamentTotal_WithTimeout()
{
if (Monitor.TryEnter(_obLock, 1000))
{
try
{
_nTotal++;
}
finally
{
Monitor.Exit(_obLock);
}
}
else
throw new SynchronizationLockException("Timeout expired on _obLock");
}
public void IncreamentTotal_WithTimeout_Better()
{
using (LockHolder lh = new LockHolder( _obLock, 1000))
{
if (lh.Locked)
{
_nTotal++;
}
} // Monitor.Exit called here
}
}
/// <summary>
/// Class to wraps Monitor.Enter/Exit calls
/// </summary>
class LockHolder : IDisposable
{
private bool _bLocked = false;
private object _obLock = null;
public LockHolder(object obLock, int nMillisecondTimeout)
{
_bLocked = Monitor.TryEnter(obLock, nMillisecondTimeout);
_obLock = obLock;
}
public bool Locked
{
get { return _bLocked; }
set { _bLocked = value; }
}
#region IDisposable Members
public void Dispose()
{
if (Locked)
{
Monitor.Exit(_obLock);
Locked = false;
}
}
#endregion
}
In object-oriented programming, you use private member variables to minimize the number of locations you need to search for state changes. In concurrent programs, you want to do the same thing by localizing the object that you use to provide synchronization. Two of the most widely used locking techniques are just plain wrong when seen from that viewpoint. lock(this) and lock(TypeOf(MyType)) have the nasty effect of creating your lock object based on a publicly accessible instance.
The best approach is to create a handle (_obLock
of type System.Object) that can be used to protect
access to the shared resources of a type, say MyType.
_obLock is a private member variable of
MyType and therefore cannot be accessed outside
MyType. You can ensure that
_obLock which is used to synchronize access is private and is not
accessible by any non-private properties of MyType.
That policy ensures that any lock primitives on a given object are local to a
given location.
If you create or use a lock inside a lambda expression, however, you must be
careful. The C# compiler creates a closure around lambda expressions. This,
combined with the deferred execution model supported by C# 3.0 constructs, means
that it will be difficult for developers to determine when the lexical scope of
the lock ends. That makes this approach more prone to deadlock issues, because
developers may not be able to determine whether code is inside a locked scope.
class Item14
{
public Item14NS.LockingDemo dnu = new Item14NS.LockingDemo();
/// <summary>
/// Bad locking strategy.
/// In this call, the calling thread acquires a lock on dnu by virtue of calling lock(this)
/// ('this' and 'dnu' represent the same object.) However, if different threads were calling
/// TestBadLockApproach1, then a deadlock situation can potentially arise as dnu is publicly
/// accessible, and different codes my actually call lock(dnu). At one point this will lead
/// to a deadlock as we do not know who is locking 'dnu'
/// </summary>
public void TestLockingApproaches()
{
dnu.MethodWithBadLock();
dnu.Method1WithGoodLock();
dnu.Method2WithGoodLock();
Item14NS.LockingDemo.StaticMethodWithGoodLock();
}
}
namespace Item14NS
{
class LockingDemo
{
private int _nSum = 0;
private object _obInstanceLock = new object();
static int _nStaticSum = 0;
static private object _obStaticLock = new object();
public void MethodWithBadLock()
{
// Do not use lock(this)
lock(this)
{
_nSum++;
}
}
public void Method1WithGoodLock()
{
// Use this approach
lock (_obInstanceLock)
{
_nSum++;
}
}
/// <summary>
/// Same as Method1WithGoodLock buts shows that there is no need to lock the
/// entire function.
/// </summary>
public void Method2WithGoodLock()
{
Func1WithNoSynchronization();
lock (_obInstanceLock)
{
_nSum++;
}
Func2WithNoSynchronization();
}
static public void StaticMethodWithGoodLock()
{
// Use this approach
lock (_obStaticLock)
{
_nStaticSum++;
}
}
private void Func1WithNoSynchronization() { /* Add any implementation here*/ }
private void Func2WithNoSynchronization() { /* Add any implementation here*/ }
}
}
If you invoke unknown code from inside a synchronized region of code, you introduce the possibility that another thread will deadlock your application. The pattern is similar. Your class acquires a lock. Then, while still in the synchronized section (i.e., inside a lock), it invokes a method (say an event) that calls code beyond your control (event handler), which in turn may call back into your class possibly on a different thread.
Look at WorkerClass class in the code
below. The RaiseProgress event notifies all
listeners of updated progress. ob_RaiseProgress
handles the RaisProgress event and calls back into
the sender to retrieve progress value. Everything works fine because the event
handler will run in the context of a background thread. However, suppose this
application were a Windows Forms application, and you
needed to marshal the event handler back to your UI thread.
Control.Invoke marshals the call to the UI thread, if necessary.
Furthermore, Control.Invoke blocks the original
thread until the target delegate has completed. Your event handler makes a
callback into the engine object in order to get the progress details. The
Progress accessor, now running on a different thread, can't acquire the same
lock. The UI thread is trying to lock the same handle already locked in the
background thread. But the background thread is suspended waiting for the event
handler to return, and the background thread already has the synch handle
locked. You're deadlocked!
Because you cannot know what actions may be taken by code outside your
control, you should try to avoid invoking the callback from inside the locked
region. In this example, this means that you must raise the
progress-reporting event from outside the locked section.
class Item15
{
public void TestUnknownCodeCallingIntoSynchedCode()
{
// Create object and hookup events.
System.Threading.Thread.CurrentThread.Name = "Item15Thread";
Item15NS.WorkerClass ob = new Item15NS.WorkerClass();
ob.RaiseProgress += new EventHandler<EventArgs>(ob_RaiseProgress);
// Call method that locks code
ob.DoWork();
}
/// <summary>
/// Event handler that calls back into the sender. Calling into the sender attempts to acquire a
/// lock that is already acquired by the code that fired the event in the first place.
/// </summary>
void ob_RaiseProgress(object sender, EventArgs e)
{
Item15NS.WorkerClass ob = sender as Item15NS.WorkerClass;
Trace.WriteLine("ThreadName: " + System.Threading.Thread.CurrentThread.Name + ". Progress: " + ob.Progress);
}
}
namespace Item15NS
{
public class WorkerClass
{
private int progressCounter = 0;
private object syncHandle = new object();
public event EventHandler<EventArgs> RaiseProgress;
public void DoWork()
{
for (int count = 0; count < 100; count++)
{
lock (syncHandle)
{
System.Threading.Thread.Sleep(100);
progressCounter++;
// An event is raised from within a syncrhonized region. Not good
// Move this block of code just outside the lock scope (see commented code)
if (RaiseProgress != null)
RaiseProgress(this, EventArgs.Empty);
}
//Comment RaiseProgress event raising inside lock and uncomment this code
// for proper operation
/*if (RaiseProgress != null)
RaiseProgress(this, EventArgs.Empty);
*/
}
}
public int Progress
{
get { lock (syncHandle) { return progressCounter;} }
}
}
}
To handle cross-thread calls in WPF, you use the
System.Windows.Threading.Dispatcher methods
Invoke() and BeginInvoke().
This is because all function calls to each control must be on the same
thread that created the control. See
Item16NS.WPFControlExtensions class.
namespace Item16NS
{
/// <summary>
/// The following ControlExtensions static class contains generic methods for any invoke delegate
/// having up to two parameters. You can add more overloads by adding more parameters. Further,
/// it contains methods that use those delegate definitions to call the target, either directly or
/// through the marshalling provided by Dispatcher
///
/// Typical usage is:
/// this.InvokeIfNeeded( (() => toolStripStatusLabel1.Text = "Test"), DispatcherPriority.Normal);
///
/// Inside the ControlExtensions class, the generic method handles the check for CheckAccess, meaning
/// that you don't need to remember it each time.
/// </summary>
public static class WPFControlExtensions
{
public static void InvokeIfNeeded(this DispatcherObject ctl, Action doit, DispatcherPriority priority)
{
if (!ctl.Dispatcher.CheckAccess())
{
ctl.Dispatcher.Invoke(priority, doit);
}
else
{
doit();
}
}
public static void InvokeIfNeeded<T>( this DispatcherObject ctl, Action<T> doit, T args, DispatcherPriority priority)
{
if (!ctl.Dispatcher.CheckAccess())
{
ctl.Dispatcher.Invoke(priority, doit, args);
}
else
{
doit(args);
}
}
}
}
C# iterators enable you to create methods that operate on a
sequence but process and returnco each element as it is requested. C# 2.0 adds
the 'yield return' statement, which lets you create
methods that return sequences. These iterator methods have a sequence as one
input (expressed as IEnumerable<T>) and produce a
sequence as output (another IEnumerable<T>). By
leveraging the 'yield return' statement, these
iterator methods do not need to allocate storage for the entire sequence of
elements. Instead, these methods ask for the next element on the input sequence
only when needed, and they produce the next value on the output sequence only
when the calling code asks for it.
This is a shift from the usual way of thinking to create
IEnumerable<T> input and output parameters. But making that shift
provides many benefits; you can apply multiple operations while iterating a
sequence only once, increasing runtime efficiency. Each iterator method executes
the code to produce the Nth element when that element is requested and not
before. This deferred execution model means that your algorithms use less
storage space and compose better than traditional imperative methods. See
TestIterators() and
TestGenericIterators() methods.
Note that iterator methods do not mutate the source sequence. Instead, they produce a new sequence as output. If the sequence contains reference types, however, the items may be modified. Multiple iterators can therefore be combined to produce complex algorithms; When you create these small iterator methods, it is much simpler to create complicated algorithms that are a single pipeline of many small transformations. See TestAllIterators() method below:
class Item17
{
/// <summary>
/// The following function shows how to use 'yield return' and generates the following output:
///
/// Entering UpdatePrice
/// Evaluating 1
///Updated price: 10
///
/// Re-entering after yield return
/// Evaluating 2
///Updated price: 20
///
/// Re-entering after yield return
/// Evaluating 3
///Updated price: 30
///
/// Re-entering after yield return
/// Evaluating 4
///Updated price: 40
///
/// Re-entering after yield return
/// Evaluating 5
///Updated price: 50
///
/// Re-entering after yield return
/// Exiting UpdatePrice
/// </summary>
public void TestIterators()
{
// Create a test list
List<int> lstPrices = new List<int>() { 1, 2, 3, 4, 5 };
// Call a function that uses 'yield return'. Observe trace outputs
foreach (int n in UpdatePrice(lstPrices, 10))
Trace.WriteLine("Updated price: " + n + Environment.NewLine);
}
/// <summary>
/// The following function shows how to use 'yield return' on a generic type,
/// and generates the following output:
/// Entering GenericUpdatePrice
/// Evaluating 1
///Updated price: -10
///
/// Re-entering after yield return
/// Evaluating 2
///Updated price: -20
///
/// Re-entering after yield return
/// Evaluating 3
///Updated price: -30
///
/// Re-entering after yield return
/// Evaluating 4
///Updated price: -40
///
/// Re-entering after yield return
/// Evaluating 5
///Updated price: -50
///
/// Re-entering after yield return
/// Exiting GenericUpdatePrice
/// </summary>
public void TestGenericIterators()
{
// Create a test list
List<int> lstPrices = new List<int>() { 1, 2, 3, 4, 5 };
foreach (int n in GenericUpdatePrice<int>(lstPrices, -10, UpdatePriceFunc))
Trace.WriteLine("Updated price: " + n + Environment.NewLine);
}
/// <summary>
/// Creating simple iterators with 'yield return' allows you to construct complex
/// pipelined algorithms
/// </summary>
public void TestAllIterators()
{
// Create a test list
List<int> lstPrices = new List<int>() { 1, 2, 3, 4, 5 };
List<int> lstTempPrices = new List<int>();
List<int> lstModifiedPrices = new List<int>();
// Call a function that uses 'yield return'. Observe trace outputs
int nFactor1 = 10;
int nFactor2 = 10;
foreach (int n in UpdatePrice(lstPrices, nFactor1))
{
lstTempPrices.Add(n);
Trace.WriteLine("Updated price: " + n + Environment.NewLine);
}
foreach (int n in GenericUpdatePrice<int>(lstTempPrices, nFactor2, UpdatePriceFunc))
{
lstModifiedPrices.Add(n);
Trace.WriteLine("Updated price: " + n + Environment.NewLine);
}
}
/// <summary>
/// The yield return statement plays an interesting trick: It returns a value and retains
/// information about its current location and the current state of its internal iteration
/// </summary>
private IEnumerable<int> UpdatePrice(IEnumerable<int> prices, int nFactor)
{
Trace.WriteLine("\tEntering UpdatePrice");
foreach (int price in prices)
{
Trace.WriteLine("\tEvaluating " + price);
yield return (price * nFactor);
Trace.WriteLine("\tRe-entering after yield return");
}
Trace.WriteLine("\tExiting UpdatePrice");
}
/// <summary>
/// A generic version of UpdatePrice() function
/// </summary>
private IEnumerable<T> GenericUpdatePrice<T>(IEnumerable<T> prices, T nFactor, Func<T, T, T> UpdatePriceFunc)
{
Trace.WriteLine("\tEntering GenericUpdatePrice");
foreach (T price in prices)
{
Trace.WriteLine("\tEvaluating " + price);
yield return UpdatePriceFunc(price, nFactor);
Trace.WriteLine("\tRe-entering after yield return");
}
Trace.WriteLine("\tExiting GenericUpdatePrice");
}
private int UpdatePriceFunc(int nPrice, int nFactor)
{
return nPrice * nFactor;
}
}
In many iteration functions (see Create Composable APIs for Sequences) you'll often find that iteration functions have two portions: a portion that iterated a sequence, and a portion that performs an action on each element in the sequence. In other words, the enumeration pattern is not related to the action performed on each item, and the two things should be done separately. Putting them together means tighter coupling and probably duplicated code.
The reason many developers combine various operations into one
method is that the portion to be customized falls between the standard opening
and closing parts of the enumeration. The only way to customize the inner
portion of such an algorithm is to pass a method call or function object to the
enclosing method. In C#, the way to do that is to use delegates to define the
inner operation. This is exactly what has been done in function
Item17.GenericUpdatePrice() in Create Composable APIs
for Sequences.
There are three main idioms that you use with anonymous delegates:
functions, actions, and predicates. A 'predicate' delegate is a Boolean
method that determines whether an element in a sequence matches some condition.
An 'action' delegate performs some action on an element in the collection. These
method signatures are so common that the .NET library contains definitions for
Action<T>, Func<T, TResult>,
and Predicate<T>:
namespace System
{
public delegate bool Predicate<T>( T obj);
public delegate void Action<T>( T obj);
public delegate TResult Func<T, TResult>(T arg);
}
TestGeneralPredicates() function below shows how to
use Predicate using anonymous delegates and lambda expressions.
TestGeneralActions() shows how to use
Action using anonymous delegates and lambda
expressions. TestDecouplingIterations() shows
illustrates the concept of decoupling iterations from actions performed on each
sequence item.
class Item18
{
/// <summary>
/// Shows predicate usage using anonymous delegates and lambda expressions
/// for List<T>.RemoveAll( predicate<T> match ) function. Internally, this
/// remove function calls your delegate method successively for every item
/// in the list. Whenever the delegate returns true, that element is removed
/// from the list.
/// </summary>
public void TestGeneralPredicates()
{
// Create a list with data
List<int> lstNumbers = new List<int>() { 1, 2, 3, 2, 3, 2, 2, 3};
// Use an anonymous delegate to remove all items that match a specific condition
// In this case, removes all elements whose value is 2
int nConditionValue = 2;
lstNumbers.RemoveAll(
delegate(int nCollectionMember)
{
return nCollectionMember == nConditionValue;
});
// Use a lambda expression to remove all items that match a specific condition
// In this case, removes all elements whose value is 3
nConditionValue = 3;
lstNumbers.RemoveAll( (int nCollectionMember) => nCollectionMember == nConditionValue );
}
/// Shows action usage using anonymous delegates and lambda expressions
/// for List<T>.ForEach( Action<T> action ) function. Internally, this
/// ForEach function calls your delegate method successively for every item
/// in the list and applies the Action function to each item
public void TestGeneralActions()
{
// Create a list with data
List<int> lstNumbers = new List<int>() { 1, 2, 3, 2, 3, 2, 2, 3 };
// Use an anonymous delegate to visit each item in lstNumbers
List<int> lstUpdatedNumbers = new List<int>();
lstNumbers.ForEach(delegate(int nCollectionMember)
{
lstUpdatedNumbers.Add(nCollectionMember * 10);
});
// Use a lambda expression to visit each item in lstNumbers
lstUpdatedNumbers.Clear();
lstNumbers.ForEach((int nCollectionMember) => lstUpdatedNumbers.Add(nCollectionMember * 10));
}
/// <summary>
/// Shows how to decouple iteration from action or logic to be applied for each item
/// </summary>
public void TestDecouplingIterations()
{
List<int> lstNumbers = new List<int>() { 1, 2, 2, 3, 4, 5, 2 };
List<int> lstFilteredNumbers = new List<int>();
int nFilterValue = 2;
foreach (int number in Filter(lstNumbers, (int nCollectionMember) => nCollectionMember != nFilterValue))
lstFilteredNumbers.Add(number);
}
/// <summary>
/// Helper method that decouples iteration logic from action to be applied to
/// each iteration item. Note that each element in the input sequence is evaluated
/// using the Predicate method and if the Predicate returns true, that element is
/// returned as part of the output sequence. ~The main point is that and developer
/// can write a method on a type that tests a condition, and the method will be
/// compatible with this filter method
/// </summary>
private IEnumerable<T> Filter<T>(IEnumerable<T> input, Predicate<T> predicate)
{
// Check we have a valid prediate
if (predicate == null) throw new ArgumentException("Predicate is null");
foreach (T item in input)
if (predicate(item))
yield return item;
}
}
It's best to generate sequence items only when each item is requested by the consumer of the sequence. You'll avoid doing extra work when the consumer needs only a portion of the algorithm to perform its work. It may be a small savings, or the savings may be much greater if the cost of creating elements is large. In any case, the code that creates the sequence will be clearer when you generate the sequence items only as needed.
class Item19
{
/// <summary>
/// Creates a sequence inefficiently. Note the following:
/// 1. First, this technique assumes that you're putting the results into a IList<double>.
/// If clients want to store the results in some other structure they must convert it.
/// 2. Creating the entire list doesn't give client code a chance to stop the generation
/// function based on a specified condition. It will always generate the requested number
/// of elements. As written, it can't be stopped if the user wants to stop the process
/// for paging or for any other reason.
/// 3. This method could be the first stage of several transformations on a sequence of data.
/// In that case, this method would be a bottleneck in the pipeline: Every element would
/// have to be created and added to the internal collection before the next step could
/// continue.
///
/// You can remove all those limitations by making the generation function an iterator method.
/// See TestCreateSequenceEfficiently() method
///
/// </summary>
public void TestCreateSequenceInefficiently()
{
IList<double> lst = CreateSequence<double>(10, 5, 10, (int nItem) => Convert.ToDouble(nItem));
}
private IList<T> CreateSequence<T>(int nElementCount, int nStartAt, int nStep, Converter<int, T> converter)
{
List<T> lst = new List<T>();
for (int i = 0; i < nElementCount; i = i + 1)
{
lst.Add(converter(nStartAt + (i*nStep)));
}
return lst;
}
/// <summary>
/// Improves on the inefficiencies of TestCreateSequenceInefficiently() by generating a sequence
/// of numbers via 'yield return'. This code does not make any assumptions regarding the storage
/// location for the generated sequence. Results can be saved in a List or a BindingList or any
/// other list
/// </summary>
public void TestCreateSequenceEfficiently()
{
Queue<double> q = new Queue<double>(CreateSequence2<double>(10, 5, 10, (int nItem) => Convert.ToDouble(nItem)));
List<double> lst = new List<double>(CreateSequence2<double>(10, 5, 10, (int nItem) => Convert.ToDouble(nItem)));
// The previous statement is equivlant to:
List<double> lst2 = new List<double>();
foreach (double item in CreateSequence2<double>(10, 5, 10, (int nItem) => Convert.ToDouble(nItem)))
lst.Add(item);
}
private IEnumerable<T> CreateSequence2<T>(int nElementCount, int nStartAt, int nStep, Converter<int, T> converter)
{
for (int i = 0; i < nElementCount; i = i + 1)
{
yield return converter(nStartAt + (i * nStep));
}
}
/// <summary>
/// It's easy to stop the enumeration if you use TakeWhile<> extension method. The sequence
/// generation short-circuits as soon as the first nonconforming value is found. That can
/// result in a significant performance improvement.
///
/// Of course, any condition can be used to determine when the enumeration should stop. You
/// can check to see whether the user wishes to continue, poll another thread for input, or
/// do anything else needed by your application. The enumerator method provides a simple
/// means of interrupting the enumeration anywhere in the sequence. This deferred execution
/// means that only the elements requested.
/// </summary>
public void TestStoppingSequenceGeneration()
{
int nStopAtValue = 20;
IEnumerable<int> collection = CreateSequence2<int>(10, 5, 10, (int nItem) => Convert.ToInt32(nItem)).
TakeWhile((int val) => val < nStopAtValue);
List<int> lst = new List<int>();
foreach (int item in collection)
lst.Add(item);
}
}
Using function parameters means that your component is not responsible for
creating the concrete type of the classes it needs. Rather, your component uses any
dependencies through an abstract definition. You can create contracts by using delegates to
minimize the requirements on client code.
First, consider one end of the spectrum where you specify base classes: Derive
from this specified base class, implement these known abstract (or other virtual) methods, and it
just works. In addition, you can implement any common functionality in the abstract base
class. Users of your component do not need to re-implement any of that code. However, all .NET
languages mandate single inheritance for classes. Creating a component that demands a base class can be
very limiting to all users. You're mandating a certain class hierarchy. There's no other way to use
it.
Now consider the other end of the spectrum where you create interfaces:
Creating interfaces and coding against them results in looser coupling than does relying on base
classes. This practice creates a relationship that's similar to using a base class. However, there
are two important differences: First, using an interface does not enforce any class hierarchy on
your users. Second, you cannot easily provide a default implementation for any of the
behavior necessary for client code.
Do you really need to define an interface or an abstract base class? Or will a
more loosely coupled approach, such as defining a delegate signature, be better?
The reason for using delegates instead of an interface is that the delegate is
not a fundamental attribute of the type. It's not the number of methods. Several interfaces in
the .NET Framework contain only one method. IComparable<T> and
IEquatable<T> are perfectly good
interface definitions. Implementing those interfaces says something about your type: that it supports
comparisons or equality. Implementing a hypothetical IPredicate<T> (see
Item20NS_DoNotUse) doesn't say
anything interesting about a particular type. Compare Item20NS_DoNotUse.List<T>.RemoveAll with
System.Collections.Generic.List<T>.RemoveAll()
which is much easier to use. However, as you loosen the coupling, you increase the amount of work you might
need to do to ensure proper error handling when these decoupled components communicate. For
example, suppose you've created code that defines events. You know that you must check that event
member against null whenever you intend to raise that event. Client code may not have created the
event handlers. You'll have the same work when you create interfaces using delegates. What is
the correct behavior when your client passes a null delegate? Is it an exception, or is there a
correct default behavior? What happens if client code delegates throw exceptions? Can you recover? If
so, how?
Also note that when you switch from using inheritance to using delegates to define your expectations, you must understand that you have the same runtime coupling you would have by holding a reference to an object or an interface. If your object stores a copy of the delegate that it can call later, your object now controls the lifetime of the object to which that delegate refers. You may be extending the lifetime of those objects. This is no different from having your object hold a reference to an object (by storing a reference to an interface or a base class) that your object will invoke later. It's a little harder to see by reading the code, though.
To summarize: The default choice is still to create interface contracts that mandate how your component will communicate with client code. Abstract base classes give you the extra ability to provide a default implementation of some of the work that otherwise would be done by client code. Defining delegates for the methods you expect gives you the most flexibility, but it also means you have less support from tools. You buy more work but gain greater flexibility.
class Item20
{
public void Test1()
{
// One way to generate a sequence. The logic to generate the sequence is
// contained within the CreateSequence method
foreach (int n in Item20NS.Utility.CreateSequence(10, 0, 5))
Trace.WriteLine(n);
// A better way to generate sequences (using lambda expressions)
int nStartAt = 0;
int nStep = 5;
IEnumerable<int> aSeq1 = Item20NS.Utility.CreateSequence_V2<int>(10, (i) => nStartAt + (i * nStep));
foreach (int n in aSeq1)
Trace.WriteLine(n);
// A better way to generate sequences (using anonymous delegates)
IEnumerable<int> aSeq2 = Item20NS.Utility.CreateSequence_V2<int>(10, delegate(int i)
{
return nStartAt + (i * nStep);
});
foreach (int n in aSeq2)
Trace.WriteLine(n);
}
public void Test2()
{
// Create a sequence that will be summed
IEnumerable<int> sequence = new int[] {1,2,3,4,5};
// A simple algorithm to add up the elements of a sequence
int nSum = Item20NS.Math.Sum(sequence);
// Same as above but creates instead a general-purpose accumulator by factoring out the sum
// operation and replacing it with a delegate;
int nSum2 = Item20NS.Math.Sum_V2<int>(sequence, (value, total) => value + total);
}
}
namespace Item20NS_DoNotUse
{
// Improper extra coupling
public interface IPredicate<T>
{
bool Match(T objectToFind);
}
public class List<T>
{
public void RemoveAll(IPredicate<T> predicate)
{
// Invoke the predicate on the collection wrapped by this instance
}
}
// This class must be implemented by the user the Item20NS.List<T> class
public class MyPredicate : IPredicate<int>
{
private int _nTarget;
public bool Match(int nValue)
{
return nValue == _nTarget;
}
}
}
namespace Item20NS
{
/// <summary>
/// The following two classes illustrate how to use functions as parameters to separate algorithms
/// from the specific data types on which they operate.
/// </summary>
public class Utility
{
/// <summary>
/// This version of CreateSequence contains the logic to create the sequence
/// based on the given parameters. Compare this method with CreateSquence2
/// </summary>
static public IEnumerable<int> CreateSequence(int nCount, int nStart, int nStep)
{
for (int i = 0; i < nCount; i++)
yield return nStart + (i * nStep);
}
/// <summary>
/// Improved on CreateSequence method above by taking a function parameter to specify
/// how the sequence should be generated
/// </summary>
static public IEnumerable<T> CreateSequence_V2<T>(int nCount, Func<int, T> generator)
{
for (int i = 0; i < nCount; i++)
yield return generator(i);
}
}
public class Math
{
static public int Sum(IEnumerable<int> sequence)
{
int nTotal = 0;
foreach (int nItem in sequence)
nTotal += nItem;
return nTotal;
}
static public T Sum_V2<T>(IEnumerable<T> sequence, Func<T, T, T> accumulator)
{
if (accumulator == null) return default(T);
T total = default(T);
foreach (T item in sequence)
total = accumulator(item, total);
return total;
}
}
}
It's easier to work with fewer overloaded methods than with more
overloads. Your goal should be to create precisely the right number of overloads: enough of
them that your type is easy for client developers to use but not so many that you
complicate the API and make it harder for the compiler to create exactly the one best method.
A generic method becomes the best method if the compiler can perform the
correct type substitution for all type parameters. Yes, even if there are obvious
candidate methods that require implicit conversions, the generic method is considered a
better method match whenever it is accessible.
namespace Item21DoNotUse
{
/// <summary>
/// Any methods having the same name should perform essentially the same function. For example,
/// the 2 Add() methods should do the same thing. However, because the two Add() methods do
/// semantically different things, then they should have different names. Therefore, when
/// overloading methods, different overloaded methods should provide different parameter lists,
/// never different actions.
///
/// Ambiguity problems arise when methods have similar arguments and the compiler must make a
/// choice. In the simplest case, there is only one parameter for any of the possible overloads
/// for the Scale() methods. By creating all these overloads, you have avoided introducing any
/// ambiguity. You probably wonder why all those Scale() overloads were not replaced with a
/// single generic method. That's because C# generics don't support that practice in the way
/// C++ templates do. With C# generics, you can't assume that arbitrary methods or operators
/// are present in the type parameters (the only methods that are available are those of
/// System.Object). You must specify your expectations using constraints. Of course, you might
/// think about using delegates to define a method constraint. But in this case, that technique
/// only moves the problem to another location in the code where both the type parameter and the
/// delegate are specified.
/// </summary>
public class Vector
{
private List<double> values = new List<double>();
/* ADD */
// Add a value to the internal list.
public void Add(double number)
{
values.Add(number);
}
// Add values to each item in the sequence.
public void Add(IEnumerable<double> sequence)
{
int index = 0;
foreach (double number in sequence)
{
if (index == values.Count)
return;
values[index++] += number;
}
}
/* SCALE */
public void Scale(short scaleFactor)
{
for (int index = 0; index < values.Count; index++)
values[index] *= scaleFactor;
}
public void Scale(int scaleFactor)
{
for (int index = 0; index < values.Count; index++)
values[index] *= scaleFactor;
}
public void Scale(float scaleFactor)
{
for (int index = 0; index < values.Count; index++)
values[index] *= scaleFactor;
}
public void Scale(double scaleFactor)
{
for (int index = 0; index < values.Count; index++)
values[index] *= scaleFactor;
}
}
}
Overloaded operators are not CLS compliant, but each operator maps to a special method name. This practice allows languages that do not support overloaded operators to invoke overloaded operators defined in languages that allow them. The names of those overloaded operators are not necessarily friendly API names. Developers using other languages would need to call a method named op_Equality to invoke the == operator you created in C#.
The simplest decision is whether to overload
operator ==. If your type overrides
System.Object.Equals or implements IEquatable<T>
for your type, you should overload operator ==.
The C# language specification mandates that if you overload
operator ==, then you must also overload operator !=
and override System.Object.GetHashCode(). All three
of those methods must follow the same semantics for equality, although the
language cannot enforce that. To enforce it, you must implement those methods
correctly. It follows that if you overload operator ==,
then you must implement IEquatable<T> and override
System.Object.Equals().
The next most common set of overloaded operators is the comparison operators. If
you implement IComparable<T> for your type, you
should also overload operator< and
operator>. If your type also implements
IEquatable<T>, you should also overload
operator <= and operator >=.
As with operator ==, the C# specification mandates
that you must overload those operators in pairs. Overloading
operator< means that you must overload
operator>, and overloading
operator <= means that you must overload
operator >=.
Notice that in both of those descriptions, the directive is to overload
operators when your type defines methods that implement the same functionality
using standard .NET interfaces. That's the most important point in this item:
Whenever you define overloaded operators, you must provide an alternative
public api that implements the same behavior. If you fail to do this, it means
that your type will be much less friendly to developers who are using languages
that don't support operator overloading. Once you move beyond the
equality and ordering relations, it's much less likely that you'll overload
operators. It's also much less clear-cut when you should overload operators.
You should consider overloading the mathematical operators if your type models
numeric data. More importantly, remember that you must provide an alternative
for languages that don't support operator overloading. Defining
operator + means that you should create an
Add() method.(System.String
has a Concat() method, which is consistent with the
semantics of + for strings.)
You'll find that it's much easier to define the set of operators you want to
overload by first defining the set of methods you want to support and then
deriving the operators you want to support based on the methods your type
supports. For example, if you create Add() and
Subtract(), you should define
operator+ and operator- but probably not any
other mathematical operators. If you define multiple Add()
methods with dissimilar parameters, you should define multiple
operator+ overloads. Furthermore, because addition
is commutative, you should define operator+ methods
with both parameter orderings. If A+B is valid, then
B+A is also valid, and it must produce the same
result even if A and B
are different types (except for System.String).
Therefore, to summarize: Start your design by defining your type's public
interface using CLS-compliant members. Once you have done that, consider
implementing overloaded operators for those languages that support types. The
most common operators you will overload are operator ==
and operator !=. You must overload those when your
type implements IEquatable<T>. Many C# developers
use the operators and expect them to behave consistently with the
IEquatable<T>.Equals() method. You'll also
overload the comparison operators: <,
>, <=, and
>=. You should implement those when your type
implements IComparable<T>.
Events can provide a way to completely decouple your class from
those types it needs to notify: You provide outgoing event definitions and
subscribers, whatever type they might be, subscribe to those events. Inside your
class, you raise the events. Your class knows nothing about the subscribers, and
it places no restrictions on the classes that can implement those interfaces.
Any code can be extended to subscribe to those events and create whatever
behavior they need when those events are raised. However, there can be some
complex coupling issues related to event-based APIs. See
Item23NS.MyClass class below.
If you need to ensure that there is exactly one subscriber, you must choose
another way of communicating with any interested code rather than using events.
For example, you can
Define an interface and call that one method, Or,
You can ask for a delegate that defines the outgoing method.
Then your single subscriber can decide whether it wants to support multiple subscribers and how to orchestrate the semantics of cancel requests.
At runtime, there's another form of coupling between event sources
and event subscribers. Your event source holds a reference to the delegate that
represents the event subscriber. The event subscriber's object lifetime now will
match the event source's object lifetime. The event source is now a root for the
event subscriber. As long as the event source holds the reference and is
reachable, the event subscriber is also reachable and therefore is not eligible
for garbage collection. Even if the event subscriber would otherwise be eligible
for destruction, the delegate handle in the event source keeps a live root, and
keeps it alive.
As a result, event subscribers need to modify their implementation of the
dispose pattern to unhook event handlers as part of the Dispose() method.
Otherwise, subscriber objects continue to live on because reachable delegates
exist in the event source object. It's another case where runtime coupling can
cost you. Even though it appears that there is looser coupling because the
compile-time dependencies are minimized, runtime coupling does have costs.
Event-based communication loosens the static coupling between types, but it comes at the cost of tighter runtime coupling between the event generator and the event subscribers. The multicast nature of events means that all subscribers must agree on a protocol for responding to the event source. The event model, in which the event source holds a reference to all subscribers, means that all subscribers must either (1)remove event handlers when the subscriber wants to be disposed of or (2) simply cease to exist. Also, the event source must unhook all event handlers when the source should cease to exist. You must factor those issues into your design decision to use events.
namespace Item23NS
{
public class MyEventArgs : EventArgs
{
private bool _bCancel;
public bool Cancel
{
get { return _bCancel; }
set { _bCancel = value; }
}
}
/// <summary>
/// MyClass contains the OnStart event whos argument type (MyEventArgs) contains a Cancel
/// flag. Suppose that there are multiple subscribters to OnStart; One subscriber might request
/// a cancel, and the second might reverse that request. The foregoing definition does not
/// guarantee that this behavior can't happen. Having multiple subscribers and a mutable event
/// argument means that the last subscriber in the chain can override every other subscriber.
/// There's no way to enforce having only one subscriber, and there is no way to guarantee that
/// you're the last subscriber. You could modify the event arguments Cancel flag to ensure that
/// once the cancel flag is set, no subscriber can turn it off (see MyEventArgs2)
/// </summary>
public class MyClass
{
private event EventHandler<MyEventArgs> OnStart;
public void RunProcess()
{
// Add code to run some specific process
// ...
// Let all subscribers know that a process has been started
MyEventArgs args = new MyEventArgs();
args.Cancel = false;
if (OnStart != null)
{
// Fires event on each subscriber that has a handler for this event
OnStart(this, args);
}
// Cancel process if a user sets MyEventArgs.Cancel to true
// But what if one user sets MyEventArgs.Cancel to true and then
// another user sets it to false? See MyEVentArgs2 class
if (args.Cancel == true)
{
// Add logic to cancel process
}
}
}
/// <summary>
/// Improves on MyEventArgs class by ensuring that once the Cancel flag is set to true, no subscriber
/// can reset it back to false. Note the use of assymetric accessor on Cancel's set property.
/// </summary>
public class MyEventArgs2 : EventArgs
{
private bool _bCancel;
public bool Cancel
{
get { return _bCancel; }
private set { _bCancel = value; }
}
public void CancelRequest()
{
Cancel = true;
}
}
}
Note 1: Read sections 10.8.1 and 10.8.4 in C# specification which
can be found at
http://msdn.microsoft.com/en-gb/vcsharp/aa336809.aspx.
Note 2: For a quick referesher on field-like events and user-defined events, see
namespace Item24NS1 below
For this item, the following is relevant 'event' information taken from the C#
specification document:
A virtual event declaration specifies that the accessors
(i.e. get and set) of that event are virtual. The virtual modifier applies
to both accessors of an event.
The accessors of an inherited virtual event can be
overridden in a derived class by including an event declaration that
specifies an override modifier. This is known as an overriding event
declaration. An overriding event declaration does not declare a new event.
Instead, it simply specializes the implementations of the accessors of an
existing virtual event.
Within the program text of the class or struct that contains the declaration of an event, certain events can be used like fields. To be used in this way, an event must not be abstract or extern, and must not explicitly include event-accessor-declarations. Such an event can be used in any context that permits a field. The field contains a delegate (§15) which refers to the list of event handlers that have been added to the event. If no event handlers have been added, the field contains null.
Item 3 means that declaring a virtual field-like event will cause
the compiler to generate a delegate field to back the event, and virtual
accessors for the event. Declaring an overriding field-like event will not cause
the compiler to generate a new backing field for the overriding event in the
case of field-like events, but will cause it to generate overriding accessors.
For a full example, see Item24NS2 and TestVirtualEvents()
method below.
The moral of the story is this: If you have a virtual event, have a
virtual triggering method. If you override a virtual event, override its
trigger method as well.
internal class Item24
{
internal void TestEvents1()
{
// Hook up events
EventDemo ob = new EventDemo();
ob.OnProgress1 += new delProgress(ob_OnProgress1);
ob.OnProgress2 += new delProgress(ob_OnProgress2);
// Call methods on 'ob' to fire events
ob.ShowProgress1();
ob.ShowProgress2();
}
internal void TestVirtualEvents()
{
// Static type is P. Run-time type is D
D derived = new D();
P parent = derived;
// Hook up handlers for case1_event through derived and parent classes
parent.case1_event += new EventHandler(parent_case1_event);
derived.case1_event += new EventHandler(derived_case1_event);
// Fire both events. Note the following:
// The compiler does not generate a protected backing field on the parent class P. Instead, it
// generates two private backing fields - one in P and one in D. Secondly, when we execute both
// functions below, we'll see that it is the derived delegate field that gets both of the handlers
// hooked up to it.
derived.DerivedCase1Event(); // Calls derived_case1_event() and parent_case1_event() handlers
parent.ParentCase1Event(); // Calls nothing as parent.case1_event is null
// Using workaround
derived.RaiseCase1Event();
parent.RaiseCase1Event();
}
void derived_case1_event(object sender, EventArgs e)
{
Trace.WriteLine("derived_case1_event");
}
void parent_case1_event(object sender, EventArgs e)
{
Trace.WriteLine("parent_case1_event");
}
void ob_OnProgress2(double dPercent)
{
Trace.WriteLine("ob_OnProgress2");
}
void ob_OnProgress1(double dPercent)
{
Trace.WriteLine("ob_OnProgress1");
}
}
namespace Item24NS
{
internal delegate void delProgress(double dPercent);
/// <summary>
/// Conceptually, an event is a pair of methods: add accessor and remove accessor.
/// Each method takes a single parameter of a specific delegate type. 'add' and
/// 'remove' accessors are invoked via 'e+=d' and 'e-=d' expressions, respectively.
/// </summary>
internal class EventDemo
{
// _del is a reference type of type delProgress
private delProgress _del;
// Declare the OnProgress1 event using field-like syntax. In this syntax, the
// event is declared with no add or remove accessors. The compiler automatically
// generates default accessors (similar to those that were manuall defined for
// OnProgress2 event), and add an anonymous private delegate that stores the
// invocation list for the event
internal event delProgress OnProgress1;
// Declare the OnProgress2 event using manual syntax. With this syntax,
// it is obvious that events are similar to properties, except that both
// accessors (add and remove) must always be present
private object _obLock = new object();
internal event delProgress OnProgress2
{
add
{
lock (_obLock) { _del += value; }
}
remove
{
lock (_obLock) { _del -= value; }
}
}
// Public inteface
public void ShowProgress1()
{
// OnProgress1 is a field-like event, but the following calls refer
// not to the event itself, but to the anonymous private delegate
// created by the compiler.
if (OnProgress1 != null)
OnProgress1(0.1);
}
public void ShowProgress2()
{
// OnProgress2 event was created manually. Note here that unlike
// OnProgress1 event, we have to use the underlying delegate for
// OnProgress2 event
if (_del != null)
_del(0.3);
}
}
internal class DerivedEventDemo : EventDemo
{
public void ShowProgress1FromDerived()
{
// The following code does not compile because the derived class does
// not have access to the compiler-generated private anonymous delegate
// Error: The event 'MoreEffectiveCS.Design.Item24NS.EventDemo.OnProgress1'
// can only appear on the left hand side of += or -= (except when used from
// within the type 'MoreEffectiveCS.Design.Item24NS.EventDemo')
/*
if (base.OnProgress1 != null)
base.OnProgress1(0.5);
*/
}
}
}
namespace Item24NS2
{
/// <summary>
/// Consider a parent class P which declares a virtual event, and some derived
/// class D which overrides it. Note that there are four combinations with respect
/// to usage of field-like or user-defined syntaxes:
/// 1. P declares a field-like event. D declares a field-like event
/// 2. P declares a field-like event. D declares a user-defined event
/// 3. P declares a user-defined event. D declares a field-like event
/// 4. P declares a user-defined event. D declares a user-defined event
///
/// </summary>
class P
{
// P contains an automatically-generated private delegate field backing case1_event,
// and two virtual accessors that add and remove from the delegate
public virtual event EventHandler case1_event;
// P contains an automatically-generated private delegate field backing case2_event,
// and two virtual accessors that add and remove from the delegate
public virtual event EventHandler case2_event;
public virtual event EventHandler case3_event
{
add { }
remove { }
}
public virtual event EventHandler case4_event
{
add { }
remove { }
}
public void ParentCase1Event()
{
// case1_event will be null!
if (case1_event != null)
case1_event(this, null);
}
public virtual void RaiseCase1Event()
{
if (case1_event != null)
case1_event(this, null);
}
}
class D : P
{
// D.case1_event contains two overriding accessors, who add and remove from the delegate
// contained in P. Notice that for this to work, the delegate in P must be elevated from
// private to protected
public override event EventHandler case1_event;
// D does not have access to P.case2_event's backing field.
public override event EventHandler case2_event
{
add { }
remove { }
}
// D has a backing field generated for case3_event, which is private
public override event EventHandler case3_event;
// This is just the typical virtual/override pattern.
public override event EventHandler case4_event
{
add { }
remove { }
}
public void DerivedCase1Event()
{
if (case1_event != null)
case1_event(this, null);
}
public override void RaiseCase1Event()
{
if (case1_event != null)
case1_event(this, null);
}
}
}
Any method that cannot perform its stated actions should report
that failure by throwing an exception. Do not use 'error codes' as these can be
ignored and can pollute the normal flow of execution with constant error checks.
Exceptions have one purpose only: to report failures. Because exceptions are
class types and you can derive your own exception types, you can use exceptions
to convey rich information about the failure.
Error return codes must be processed by the method caller. In contrast, thrown
exceptions
propagate up the call stack until a suitable catch clause is found. That gives
developers
the freedom to isolate error handling from error generation by many levels in
the call
stack. No error information is lost in this separation because of the richness
of your
exception classes.
Finally and most importantly, exceptions cannot be ignored. If a program does not contain a suitable catch clause, then a thrown exception terminates the application. You can't continue running after an unnoticed failure, something that would cause data corruption.
Using exceptions to report contract failures does not mean that any method that cannot do what you want must exit by throwing an exception. For example, consider File.Exists() and File.Open(): File.Open() throws an exception if a file is not found, however, File.Exists() does not throw exceptions at all, it just examines if a file exists and returns true or false.
How does this information affect your designs: You should create methods in your classes that enable users to test possible failure conditions before performing work. This practice lets developers program more defensively, and you can still throw exceptions if developers choose not to test conditions before calling methods.
class Item25
{
public void DoNotPromoteThisPattern()
{
OrderManager om = new OrderManager();
try
{
om.ProcessOrder(10);
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
}
}
public void PromoteThisPattern()
{
OrderManager om = new OrderManager();
try
{
if (!om.TryProcessOrder(10))
{
Trace.WriteLine("Order is invalid");
}
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
}
}
}
namespace Item25NS
{
/// <summary>
/// This pattern requires you to write four methods: two public methods, and two private methods.
/// The TryProcessOrder() method validates all input parameters and any internal object state
/// necessary to perform the work. Then it calls the DoProcessOrder() method to perform the task.
/// ProcessOrder() simply calls the DoProcessOrder() method and lets any failures generate exceptions.
/// This idiom is used in .NET because there are performance implications involved in throwing
/// exceptions, and developers may wish to avoid those costs by testing conditions before allowing
/// methods to fail.
/// </summary>
class OrderManager
{
#region Public interface
public void ProcessOrder(int nOrderID)
{
DoProcessOrder(nOrderID);
}
public bool TryProcessOrder(int nOrderID)
{
// Is the order valid
if (!IsOrderValid(nOrderID)) return false;
// We have a valid order. Process the order
DoProcessOrder(nOrderID);
return true;
}
#endregion
#region Helpers
/// <summary>
/// Processes an order. Throws exceptions if it fails.
/// </summary>
private void DoProcessOrder(int nOrderID)
{
// Do we have a valid order ID
if (nOrderID == -1)
throw new InvalidOperationException("Received invalid order ID");
// Continue processing order
// ...
}
/// <summary>
/// Checks that an order exists
/// </summary>
private bool IsOrderValid(int nOrderID)
{
// Do we have a valid order ID
if (nOrderID == -1) return false;
// Check order against database
// ...
return true;
}
#endregion
}
}
When developers use properties they generally expect the following:
get/set properties should never be expensive operations (i.e., should not call databases or remote code)
get/set properties are expected to do some validations.
These expectations arise as a result of viewing properties as data.
Properties are generally accessed in tight loops; consider for example using
Length property on an array:
for (int i = 0; i < myarray.Length; i++ ) ...
You would have a very poor performance if you had to count all elements in the
array every time you accessed the array's length. To properly use properties:
Use 'Automatically Implemented Properties'. See Item26NS.Point and Item26NS.ReadOnlyPoint.
Add validation to property setters. See Item26NS.Person.
Use Caching to improve performance for property get accessors. See Item26NS.Averager.
Expensive or lengthy property getter/setters should be redesigned as public methods. See Item26NS.OrderManager_DoNotUse and Item26NS.OrderManager_Use.
class Item26
{
public void TestAutoProperties()
{
// Using 'Automatically Implemented Properties'
Point pt = new Point();
pt.X = 10;
pt.Y = 20;
ReadOnlyPoint rpt = new ReadOnlyPoint(1, 2);
int x = rpt.X;
//rpt.X = 10; // ERROR: The property or indexer 'ReadOnlyPoint.X' cannot be used in
// this context because the set accessor is inaccessible
}
public void TestPropertyCaching()
{
// Using caching to imrpove property performance
Averager<int> avg = new Averager<int>();
avg.AddNumber(10);
avg.AddNumber(20);
double dAvg1 = avg.Average; // Average calculated
dAvg1 = avg.Average; // Average obtained from cache
avg.AddNumber(30);
dAvg1 = avg.Average; // Average calculated
}
public void TestPropertyValidation()
{
// Using validation in properties
try
{
Person p = new Person();
p.LastName = ""; // Throws an exception
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
}
}
}
namespace Item26NS
{
/// <summary>
/// Use 'Automatically Implemented Properties' whenever possible. When a property is
/// specified as an automatically implemented property, a hidden backing field is
/// automatically available for the property, and the accessors are implemented to
/// read from and write to that backing field.
///
/// Because the backing field is inaccessible, it can be read and written only through
/// the property accessors. This means that automatically implemented read-only or
/// write-only properties do not make sense, and are disallowed. See Point class
///
/// It is however possible to set the access level of each accessor differently. Thus,
/// the effect of a read-only property with a private backing field can be mimicked. See
/// ReadOnlyPoint class
/// </summary>
class Point
{
public int X { get; set; } // automatically implemented
public int Y { get; set; } // automatically implemented
}
class ReadOnlyPoint
{
public ReadOnlyPoint(int x, int y) { X = x; Y = y; }
public int X { get; private set; }
public int Y { get; private set; }
}
/// <summary>
/// 'Automatically Implemented Properties' do not support validation. In this case
/// implement properties using an explicitly-specified backing store and add
/// validation to each property setter. Validation code usually does not break
/// any of the fundamental propery assumption listed at the top of this item.
/// </summary>
class Person
{
private string _strLastName;
public string LastName
{
get { return _strLastName; }
set
{
// Check we have a valid last name
if (string.IsNullOrEmpty(value))
throw new ArgumentException("Last name cannot be null or empty");
_strLastName = value;
}
}
}
/// <summary>
/// Shows how to use caching to improve property performance. The 'Average' property
/// is recalculated only if new values have been added to a list of numbers
/// </summary>
class Averager<T>
{
private List<T> lstNumbers = new List<T>();
private double _dAverage;
private bool _bCacheInvalid = true;
// Add a number to the list of numbers and invalidate the cache
public void AddNumber(T number)
{
lstNumbers.Add(number);
_bCacheInvalid = true;
}
public double Average
{
get
{
// Compute only if cache is invalid
if (_bCacheInvalid)
{
_dAverage = lstNumbers.Average(num => Convert.ToDouble(num));
_bCacheInvalid = false;
}
// Return average
return _dAverage;
}
}
}
/// <summary>
/// Shows that expensive property operation should be redesigned as public methods
/// </summary>
class Order
{
public int ID { get; set; }
public int Value { get; set; }
}
class OrderManager_DoNotUse
{
public int OrderID { get; set; }
public Order Order
{
get
{
return RetrieveOrderFromRemoteDatabase(OrderID);
}
set
{
SaveOrderInRemoteDatabase( value );
}
}
private void SaveOrderInRemoteDatabase( Order Order)
{
// Save order to a remote database
}
private Order RetrieveOrderFromRemoteDatabase(int nOrderID)
{
// Get order from database
return new Order();
}
}
class OrderManager_Use
{
public int OrderID { get; set; }
public void SaveOrderInRemoteDatabase(Order Order)
{
// Save order to a remote database
}
public Order RetrieveOrderFromRemoteDatabase()
{
// Get order from database
return new Order();
}
}
}
Recall that C# only supports public inheritance from a single
class. Also recall that public inheritance is referred as 'IS A' while
composition (or containment) is referred to as 'HAS A' Both inheritance (IS
A) and composition (HAS A) provide ways to reuse implementation from another
class. Inheritance is not always the best representation of a design. Inheritance creates the tightest coupling between two classes. The derived class
contains all the members of the base class. And because the derived class IS A
instance of the base class, an object of the derived class type can be
substituted for an object
of the base class at any time. Composition (or containment) on the other hand,
allows the outer object to expose selected methods from the inner object,
without such tight coupling of the public and protected interfaces.
Your class's interface is also insulated against future updates of the inner
class. Using inheritance, upgrades to base types are automatically part of your
class's interface. Using composition, you must explicitly modify your outer
class in order to expose any new methods in the inner class. This constraint may
be important if the inner class was created by a third party. Your user
community can get newer versions of those components without getting an upgrade
of your system.
See Item27NS1 namespace below for classes that use both inheritance and
containment. Item27NS1.EmployeeWithComposition class has lost polymorphism
because it does not derive from Person: You cannot substitute an instance of
Item27NS1.EmployeeWithComposition when an instance of
Person is expected. However, using
interfaces you can 're-establish' polymorphism as shown in classes in
Item27NS2 namespace
Finally, containment lets you change the inner class at runtime. You can examine
various runtime conditions and create various inner classes. That requires you
to have defined interfaces that your inner classes implement. It allows you to
treat different inner classes polymorphically. See Item27NS3 namespace for an
example.
From these discussions, you should strive to expand your set of design choices and use both composition and inheritance. When you create types that reuse implementation from other types, you should use composition. If your types model an IS A relationship in every way, inheritance is the better choice. Composition requires more work to expose the implementation from the inner objects, but the payoff is more control over the coupling between your type and the type whose implementation you wish to reuse. Using inheritance means that your derived type is a special case of the base class in every way.
class Item27
{
/// <summary>
/// Shows differences between inheritance and composition. EmployeeWithInheritance inherits
/// all public methods of Person, while EmployeeWithComposition contains an instance of Person
/// and selectively implements specific methods from Person
/// </summary>
public void TestCompositionVsInheritance()
{
// Base class
Item27NS1.Person person = new Item27NS1.Person();
person.FirstName = "Yazan";
// Derived class
Item27NS1.EmployeeWithInheritance empI = new Item27NS1.EmployeeWithInheritance();
empI.DateOfEmployment = DateTime.Now;
empI.FirstName = "Yazan";
empI.LastName = "Diranieh";
empI.DOB = DateTime.Now.AddYears(-30);
// Class with composition (note that DOB is not available)
Item27NS1.EmployeeWithComposition empC = new Item27NS1.EmployeeWithComposition();
empC.DateOfEmployment = DateTime.Now;
empC.FirstName = "Yazan";
empC.LastName = "Diranieh";
}
/// <summary>
/// Both Person and EmployeeWithContainment implement IPerson interface to 're-establish'
/// polymorphism that was lost in the classes used in TestCompositionVsInheritance() method
/// </summary>
public void TestCompotionViaInterface()
{
// Create a Person instance
Item27NS2.Person person = new Item27NS2.Person();
person.DOB = DateTime.Now;
person.FirstName = "Yazan";
person.LastName = "Diranieh";
// Create an EmployeeWithContainment instance
Item27NS2.EmployeeWithContainment emp = new Item27NS2.EmployeeWithContainment();
emp.DateOfEmployment = DateTime.Now;
emp.FirstName = "Yazan";
emp.LastName = "Diranieh";
emp.DOB = DateTime.Now.AddYears(-30);
// Note that PrcoessPerson accepts both 'emp' and 'person' instances as they both
// implement the IPerson interface
PrcoessPerson(person);
PrcoessPerson(emp);
}
private void PrcoessPerson(Item27NS2.IPerson person)
{
Trace.WriteLine("FirstName: " + person.FirstName + Environment.NewLine +
"LastName: " + person.LastName + Environment.NewLine +
"DOB: " + person.DOB + Environment.NewLine);
}
/// <summary>
/// Shows how the use interface allows a containing class to support different
/// inner classes implementations at runtime
/// </summary>
public void TestCompositionRuntimeChanges()
{
try
{
// Uses Person1 implementation. Does not throw exception when FirstName is empty
Item27NS3.EmployeeWithContainment emp1 = new Item27NS3.EmployeeWithContainment(1);
emp1.FirstName = "";
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
}
try
{
// Uses Person2 implementation. Throws an exception when FirstName is empty
Item27NS3.EmployeeWithContainment emp = new MoreEffectiveCS.Design.Item27NS3.EmployeeWithContainment(2);
emp.FirstName = "";
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
}
}
}
namespace Item27NS1
{
/// <summary>
/// Base class that exposes a few public properties
/// </summary>
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DOB { get; set; }
}
/// <summary>
/// Derived class that inherits publicly from Person. EmployeeWithInheritance inherits
/// all public members (properties, fields, methods, events, etc.) of the base class
/// </summary>
class EmployeeWithInheritance : Person
{
public DateTime DateOfEmployment { get; set;}
}
/// <summary>
/// A class that contains (has a) Person object rather than derive from Person.
/// With containment (i.e., composition) you can selectively choose which, if any,
/// of the public members available in the inner class are exposed as part of the
/// containing (outer) class. Note that public members of the contained class (Person)
/// are implemented by delegating to the contained instance of Person.
///
/// </summary>
class EmployeeWithComposition
{
// Contained class. Could also be passed via a constructor
private Person _person = new Person();
// Public interface for EmployeeWithComposition
public DateTime DateOfEmployment { get; set; }
// The following two properties are delegated to the contained object
public string FirstName
{
get { return _person.FirstName; }
set { _person.FirstName = value; }
}
public string LastName
{
get { return _person.LastName; }
set { _person.LastName = value; }
}
}
}
namespace Item27NS2
{
/// <summary>
/// Interface IPerson is supported by two Person and EmployeeWithContainment classes.
/// EmployeeWithContainment delegats all its IPerson methods to its contained Person
/// class. EmployeeWithContainment cannot be used by a method that expects a Person
/// object, but both EmployeeWithContainment and Person object can be used by methods
/// that expect an IPerson object. Note that the concept of refactoring methods into
/// interfaces makes it easier to create outer classes that aggregate more than one
/// inner type. For example, you can create another interface IEmployee and have
/// EmployeeWithContainment implement IPerson and IEmployee
/// </summary>
public interface IPerson
{
string FirstName { get; set; }
string LastName { get; set; }
DateTime DOB { get; set; }
}
/// <summary>
/// Implements IPerson
/// </summary>
public class Person : IPerson
{
#region IPerson Members
public string FirstName {get; set;}
public string LastName {get; set;}
public DateTime DOB {get; set;}
#endregion
}
/// <summary>
/// Implement IPerson by delegating to an inner Person object
/// </summary>
public class EmployeeWithContainment : IPerson
{
// Contained class. Could also be passed via a constructor
private Person _person = new Person();
// Publi interface
public DateTime DateOfEmployment { get; set; }
#region IPerson Members
public string FirstName
{
get { return _person.FirstName; }
set { _person.FirstName = value; }
}
public string LastName
{
get { return _person.LastName; }
set { _person.LastName = value; }
}
public DateTime DOB
{
get { return _person.DOB; }
set { _person.DOB = value; }
}
#endregion
}
}
namespace Item27NS3
{
interface IPerson
{
string FirstName { get; set; }
string LastName { get; set; }
DateTime DOB { get; set; }
}
/// <summary>
/// Implements IPerson
/// </summary>
class Person1 : IPerson
{
#region IPerson Members
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DOB { get; set; }
#endregion
}
/// <summary>
/// Same as Person1 except that it now adds validations
/// </summary>
class Person2 : IPerson
{
// Backing stores for IPerson properties
private string _strFirstName = string.Empty;
private string _strLastName = string.Empty;
#region IPerson Members
public string FirstName
{
get { return _strFirstName; }
set
{
if (string.IsNullOrEmpty( value ))
throw new ArgumentException("FirstName cannot be null or empty");
_strFirstName = value;
}
}
public string LastName
{
get { return _strLastName; }
set
{
if (string.IsNullOrEmpty( value ))
throw new ArgumentException("LastName cannot be null or empty");
_strLastName = value;
}
}
public DateTime DOB { get; set; }
#endregion
}
/// <summary>
/// Implement IPerson by delegating to an inner Person1 object or Person2 object,
/// based on a version number that is passed at runtime. This allows EmployeeWithContainment
/// class to change its runtime behavior based on which version is required
/// </summary>
class EmployeeWithContainment : IPerson
{
// Contained class. Could also be passed via a constructor
private Person1 _person1 = new Person1();
private Person2 _person2 = new Person2();
private int _version = 0;
public EmployeeWithContainment(int version)
{
_version = version;
}
// Publi interface
public DateTime DateOfEmployment { get; set; }
#region IPerson Members
public string FirstName
{
get { return (_version == 1) ? _person1.FirstName : _person2.FirstName; }
set
{
if (_version == 1)
_person1.FirstName = value;
else
_person2.FirstName = value;
}
}
public string LastName
{
get { return (_version == 1) ? _person1.LastName : _person2.LastName; }
set
{
if (_version == 1)
_person1.LastName = value;
else
_person2.LastName = value;
}
}
public DateTime DOB
{
get { return (_version == 1) ? _person1.DOB : _person2.DOB; }
set
{
if (_version == 1)
_person1.DOB = value;
else
_person2.DOB = value;
}
}
#endregion
}
}
Recall that extension methods are used to extend existing types and constructed types with additional methods. You can define an interface with minimal capabilities and then create a set of extension methods defined on that interface to extend its capabilities. In particular, you can add behavior instead of just defining an API.
Syntactically, you create a static class with static methods that have a specific signature: the first parameter of any extension method must be the this keyword, which identifies the type on which the extension method will appear. System.Linq.Enumerable class provides a great example of this technique. This class contains more than 50 extension methods defined on IEnumerable<T>. Those methods range from Where to OrderBy to ThenBy to GroupInto. Defining these as extension methods on IEnumerable<T> provides great advantages. First, none of these capabilities requires modifications to any class that already implements IEnumerable<T>. No additional responsibilities have been added for classes that implement IEnumerable<T>. However, Implementers still need define only GetEnumerator(), and IEnumerator<T> still need define only Current, MoveNext(), and Reset(). Yet by creating extension methods, the C# compiler ensures that all collections now support query operations.
You can follow the same pattern yourself. For example, IComparable<T> follows a pattern from the days of C. If left < right, then left.CompareTo(right) returns a value less than 0. If left > right, then left.CompareT (right) returns a value greater than 0. When left and right are equivalent, left.CompareTo(right) returns 0. This pattern has been used so often that many of us have it memorized, but that doesn't make it very approachable. It would be much more readable to write something like left.LessThan(right) or left.GreaterThanEqual(right). That's easy to do with extension methods. See method TestSimpleExtensionMethod().
You should also follow the same pattern as defined in Item28NS with interfaces you've created in your applications. Rather than define rich interfaces, use the interface to define the minimal functionality necessary to satisfy the requirements. ANY CONVENIENCE METHODS THAT CAN BE BUILT ON THAT MINIMAL INTERFACE SHOULD BE CREATED USING EXTENSION METHODS. Compared with richer interface contracts, using extension methods enables implementers to write fewer methods and still provide a richer interface to client code.
class Item28
{
public void TestSimpleExtensionMethod()
{
Person p1 = new Person( new DateTime(1990,1,1));
Person p2 = new Person(new DateTime(2000, 1, 1));
bool bLessThan = p1.LessThan(p2);
bool bGreaterThan = p1.GreaterThan(p2);
}
}
namespace Item28NS
{
/// <summary>
/// Similar in purpose to System.Linq.Enumerable. Provides a set of extension methods
/// on IComparable<T>. Note that the class is static, meaning that all its members
/// are also static
/// </summary>
static class Comparable
{
public static bool LessThan<T>(this T left, T right) where T : IComparable<T>
{
return (left.CompareTo(right) < 0);
}
public static bool GreaterThan<T>(this T left, T right) where T : IComparable<T>
{
return (left.CompareTo(right) > 0);
}
public static bool GreaterThanOrEqual<T>(this T left, T right) where T : IComparable<T>
{
return (left.CompareTo(right) >= 0);
}
public static bool LessThanOrEqual<T>(this T left, T right) where T : IComparable<T>
{
return (left.CompareTo(right) <= 0);
}
}
/// <summary>
/// A simple class that implements IComparable<T> on date of birth. In fact, every class that
/// implements IComparable<T> now appears to include these additional methods if the proper
/// using declaration is in scope. Implementers still need only create one method (CompareTo),
/// and the client code can use other, easier-to-read signatures.
///
/// </summary>
public class Person : IComparable<Person>
{
public DateTime DOB { get; set; }
public Person(DateTime dob)
{
DOB = dob;
}
#region IComparable<Person> Members
public int CompareTo(Person other)
{
return DateTime.Compare(DOB, other.DOB);
}
#endregion
}
}
First a quick definition. A constructed type is a generic type with at least one type argument. For example, given the generic type List<T>, List<int>, List<string>, List<MyType>, etc are all constructed types. An open constructed type is created when at least one type argument is left unspecified, while a closed constructed type is created by specifying all type arguments. For example given generic type LinkedList<K,T>:
LinkedList<int, T> lst1;
// lst1 is an open constructed type
LinkedList<int, long> lst2;
// lst2 is a closed constructed type
Item 28 discussed System.Linq.Enumerable class and how it extends IEnumerable<T> with many common methods on sequences. In addition, System.Linq.Enumerable contains a number of methods that are implemented specifically for particular 'constructed types' that implement IEnumerable<T>. For example, several numeric methods are implemented on numeric sequences (IEnumerable<int>, IEnumerable<double>, IEnumerable<long>, and IEnumerable<float>). Here are a few of the extension methods implemented specifically for IEnumerable<int>:
public class Enumerable
{
public static int Average(this IEnumerable<int> sequence);
public static int Max(this IEnumerable<int> sequence);
public static int Min(this IEnumerable<int> sequence);
public static int Sum(this IEnumerable<int> sequence);
// other methods ...
}
Once you recognize the pattern, you can see many ways you could implement the
same kind of extensions for your own constructed types. If you didn't have
extension methods, you could achieve a similar effect by deriving a new type
from the constructed generic type you used. See notes for
Item29NS_DoNotUse below:
class Item29
{
public void TestMethodExtensions()
{
// Create a collection customers
List<Customer> lstCustomers = new List<Customer>();
lstCustomers.Add(new Customer(1, "x", "y"));
lstCustomers.Add(new Customer(2, "a", "b"));
lstCustomers.Add(new Customer(3, "g", "h"));
// Now how extension methods are available on 'lstCustomers' constructed type
lstCustomers.SendCustomerEmail("this is a test message");
lstCustomers.PrintAllCustomerDetails();
}
}
namespace Item29NS
{
class Customer
{
public int CustomerID {get; set;}
public string FirstName { get; set; }
public string LastName { get; set; }
public Customer(int id, string fn, string ln)
{
CustomerID = id;
FirstName = fn;
LastName = ln;
}
public void AddOrder()
{
// Method to assocaite order with this customer
}
}
static class CustomerExtensions
{
public static void SendCustomerEmail(this IEnumerable<Customer> customers, string strMessage)
{
Trace.WriteLine("Implement SendCustomerEmail to send message to all customers");
}
public static void PrintAllCustomerDetails(this IEnumerable<Customer> customers)
{
Trace.WriteLine("PrintAllCustomerDetails to print all customer details");
}
}
}
namespace Item29NS_DoNotUse
{
/// <summary>
/// The extension methods in class Item29NS.CustomerExtensions use IEnumerable<Customer> as the parameter,
/// but the methods added to CustomerList class below are based on List<Customer>. They mandate a particular
/// storage model. For that reason, they can't be composed as a set of iterator methods
/// </summary>
class CustomerList : List<Customer>
{
public void SendCustomerEmail(string strMessage)
{
Trace.WriteLine("Implement SendCustomerEmail to send message to all customers");
}
public void PrintAllCustomerDetails()
{
Trace.WriteLine("PrintAllCustomerDetails to print all customer details");
}
}
}
There are times when it's better to use implicitly typed variables, because the compiler will pick a better type than you will. Recall that the compiler examines the compile-time type and infers the type of the local variable based on those declarations. At other times, however, overusing var only decreases the readability of your code. Even worse, using implicitly typed variables can lead to subtle conversion bugs.
Many times, the type of a local variable is clear from its initialization statement:
var foo = new MyType();
However, in some cases, the return type might not always be clear from the method name:
var foo = someObject.DoSomeWork(anotherParameter);
Therefore, introducing var for a variable returned from some method is one of
the ways that the use of var can be confusing.
When you work with built-in numeric types, using implicitly typed variables can
lead to
conversion bugs because many implicit conversions are available on numeric
types. However
by explicitly declaring the types of all numeric variables, you retain some
control over
the types used, and you help the compiler warn you about possible dangerous
conversions.
Variables declared with var are not dynamic but instead are
implicitly declared
with the
type of the right side of the assignment. You are not telling the compiler which
type
you're creating; the compiler declares the type for you. When the type you want
is not
the same as the type the compiler would pick, you introduce problems in your
code.
Sometimes, though, the compiler may be smarter than you are. See TestEnumerableAndQueryable()
method below.
class Item30
{
/// <summary>
/// Consider q1 and q2 queries below.
///
/// Because the query is running against a database, it's actually in IQueryable<string>.
/// However, by strongly declaring the return value, you've lost that information. IQueryable<T>
/// derives from IEnumerable<T>, so the compiler does not even warn you about this assignment
/// which has a serious performance problems. Now when the second portion of the query is
/// compiled, Enumerable.Where will be used, rather than Queryable.Where. That has a large
/// negative implication for performance. In Item38 you will note that IQueryable composes
///
multiple query expression trees into a single
operation that can be executed at once,
///
often at the remote server where the data
is located. In this instance the second
portion
///
of the query (the where clause) sees the source as an IEnumerable<string>.
That change is
///
significant, because only the first part of the query is built on the remote
machine. The
/// entire
list of customer names will be returned from the source. The second
statement (the
///
where clause) locally examines the entire list of customer contact names and
returns only
/// those matching the search string.
///
/// Now consider q3 and q4 queries below.
///
/// q3 is an IQueryable<Employee>. The compiler infers the return type because of the source
/// of the query. The second statement composes the query, adding the Where clause to the query,
/// and holds a new, more complete expression tree. The actual data is retrieved only when the
/// caller examines the query. The expression to filter the query gets passed to the data source,
///
and the result sequence contains only
those employees that match the filter. Any
network
///
traffic is reduced, and the query is more efficient. This miraculous change
is that q3 is
///
now declared (by the compiler) as IQueryable<Employee>
instead of IEnumerable<Employee>
///
/// To summarize: it's best to declare local variables using var unless developers (including
/// you, in the future) need to see the declared type to understand the code. However, explicitly
/// declare all numeric types (int, float, double, and others) rather than use var declarations.
/// </summary>
public void TestEnumerableAndQueryable()
{
// Create a context to the AdvanetureWorks database
AdventureWorksLINQDataContext ctxt = new AdventureWorksLINQDataContext();
// IEnumerable query
IEnumerable<Employee> q1 = from e in ctxt.Employees select e;
var q2 = q1.Where((Employee e) => e.Gender == 'M');
foreach (var e in q2)
Trace.WriteLine("ID: " + e.EmployeeID + ", DOB: " + e.BirthDate);
// IQueryable query
Trace.WriteLine("");
var q3 = from e in ctxt.Employees select e;
var q4 = q3.Where((Employee e) => e.Gender == 'M');
foreach (var e in q4)
Trace.WriteLine("ID: " + e.EmployeeID + ", DOB: " + e.BirthDate);
}
}
You should use anonymous types whenever you need simple data containers that store interim results. Anonymous types are scoped inside the method where they are declared. Using an anonymous type clearly shows other developers that a particular type is scoped inside that single method.
Anonymous types save you quite a bit of work and help you gain features
because the compiler can generate some code you can't. Consider the following
anonymous object initializer:
var point = new {X=1, Y=2};
The declaration above implies the following:
You've told the compiler to write something like this for you:
internal sealed class __Anonymous
{
private readonly int x;
public int X
{
get { return X; }
}
private readonly int y;
public int Y
{
get { return y; }
}
public __Anonymous(int xParm, int yParm)
{
x = xParm;
y = yParm;
}
}
It is better to let the compiler write this rather than manually code it for the
following reasons:
Also note that anonymous types support
IEquatable<T> and nothing else. So you can assume the existence only of
the System.Object public members and the
IEquatable<T> members.
The obvious drawback of using anonymous types is that you don't know the name of
the type. Because you don't name the type, you can't use an anonymous type as a
parameter to a method or as its return value. However, if you mix in generic
methods that contain function parameters, you can create methods that work with
anonymous types. The scope of an anonymous type is limited to the method where
it is defined. Using generic methods and lambdas means that you can define any
necessary transformations on those anonymous types within the scope of the
method where the anonymous type is defined. See TestBasicAnonymousType() method
below.
class Item31
{
public void TestBasicAnonymousType()
{
// Error: Cannot assign <null> to anonymous type property
//var Employee = new { ID = 1, Name= null};
// The use of a generic method that has a function parameter allows you to pass
// in anonymous types
var point = new { X = 1, Y = 2 };
var point2 = Transform( point, (pt) => new {X=pt.X * 10, Y=pt.Y * 10});
}
private T Transform<T>(T element, Func<T, T> transformFunc)
{
return transformFunc(element);
}
}
Composable methods relate to methods on objects that act as input
parameters. Specifically composable methods are member methods of one of
the input parameters, and they return a
result that is composed from the transformation involved in the method.
In the code below, compare methods in NonComposableMethods class with the much
better methods in ComposableMethods.
See BadExtensionMethods which illustrate some of the basic scenarios where
extension methods are abused
Extension methods are a great way to massage the signature of an API so that it
can be composed into complicated expressions. However, when you create extension
methods to change the composability of an API, you need to ensure that the API
doesn't hide errors nor change the performance metrics of an implied API.
class Item32
{
/// <summary>
/// Tests composable vs. non-composable methods. End results are the same but composable
/// methods can be easily enhanced without affecting overall method semantics
/// </summary>
public void TestComposableMethods()
{
// Read CSV file. Content is:
//
1,2,3,4,5
//
6,7,8,9,10
//
11,12,999
StreamReader reader = new StreamReader(@"D:\Projects\MoreEffectiveCS\MoreEffectiveCS\C#3\Item31Input.txt");
// Parse CSV file using a non-composable method
NonComposableMethods obNonComp = new NonComposableMethods();
List<List<int>> lstLines1 = obNonComp.CSVParser(reader);
PrintResults(lstLines1);
// Parse CSV file using a composable method
ComposableMethods obComp = new ComposableMethods();
reader.BaseStream.Seek(0, SeekOrigin.Begin);
IEnumerable<IEnumerable<int>> lstLines2 = obComp.SimpleCSVParser(reader);
PrintResults(lstLines2);
// Parse CSV file using a composable method that detects invalid input
reader.BaseStream.Seek(0, SeekOrigin.Begin);
IEnumerable<IEnumerable<int>> lstLines3 = obComp.CSVParserWithDetect(reader);
PrintResults(lstLines3);
}
/// <summary>
/// Illustrates a few bad examples of extension methods
/// </summary>
public void TestBadExtentions()
{
// GetStringLength() is an extension method that works with nulls. BAD!
string name = null;
name.GetStringLength();
// Inefficient extension method
List<int> lst = new List<int>{1,2,3,4};
List<int> lstReversed = lst.InefficientReverse().ToList();
// Using an array to call an AddRange extension method
string[] numbers = { "one", "two", "three" };
string[] range = { "four", "five"};
numbers.AddRange(range);
}
private void PrintResults(IEnumerable<IEnumerable<int>> lines)
{
foreach (IEnumerable<int> a in lines)
{
foreach (int n in a)
Trace.Write(n + ",");
Trace.WriteLine("");
}
}
private void PrintResults(List<List<int>> lines)
{
foreach (List<int> a in lines)
{
foreach (int n in a)
Trace.Write(n + ",");
Trace.WriteLine("");
}
}
}
namespace Item32NS
{
/// <summary>
/// A class illustrating non-composable methods
/// </summary>
public class NonComposableMethods
{
/// <summary>
/// CSVParser reads a CSV file of numeric data and returns a list of arrays, where each array
/// contains the values in one line
///
/// This implementation works as requried but does not support composition. Suppose the files
/// were very large and contained marker values that would indicate when you could stop reading
/// the file. To modify this method for new requirements, you'd need to add parameters, add logic
/// to support changes, and increase the overall complexity of the method. Because you've chosen
/// an imperative model, whenever any changes are needed you must change the description of how
/// the method works. WHAT'S NEEDED IS TO MODITY THE API SIGNATURES TO PROVIDE COMPOSABILITY OF
/// THE ACTIONS, SOMETHING THAT WILL ALLOW CALLERS TO MODIFY THE BEHAVIOR BY USING DIFFERENT
/// EXPRESSIONS.
/// </summary>
public List<List<int>> CSVParser(TextReader src)
{
// Required variables
List<List<int>> lstLinesAsNumbers = new List<List<int>>();
string strCurrentLineAsCSVLine = string.Empty;
string[] astrCurrentLineAsArray;
int nDataValue = 0;
// Read all lines until we get a null
while ((strCurrentLineAsCSVLine = src.ReadLine()) != null)
{
// Split current line into an array of strings
astrCurrentLineAsArray = strCurrentLineAsCSVLine.Split(new char[] { ',' });
// Now parse into numbers
List<int> lstCurrentLine = new List<int>();
foreach (var value in astrCurrentLineAsArray)
{
nDataValue = 0;
int.TryParse(value, out nDataValue);
lstCurrentLine.Add(nDataValue);
}
lstLinesAsNumbers.Add(lstCurrentLine);
}
return lstLinesAsNumbers;
}
}
/// <summary>
/// Contains composable methods for parsing lines.
/// Note that even though the method signatures have changed (compared to NonComposableMethods.CSVParser),
/// the underlying semantics have not. That's an important consideration when you create extension methods.
/// Be sure not to change the caller's assumption about the behavior of the objects involved in the extension
/// method.
/// </summary>
public class ComposableMethods
{
/// <summary>
/// Same as NonComposableMethods.CSVParser except that SimpleCSVParser is now
/// composable
/// </summary>
public IEnumerable<IEnumerable<int>> SimpleCSVParser(TextReader src)
{
var lines = from line in src.ReadLines() select line.ParseLine(0);
return lines;
}
/// <summary>
/// Same as SimpleCSVParser except that it now it reads until it finds an invalid
/// value which is indicated by 999. All this is possible because we've used extension
/// methods to modify the API signature into a composable form
/// </summary>
public IEnumerable<IEnumerable<int>> CSVParserWithDetect(TextReader src)
{
var lines = (from line in src.ReadLines()
select line.ParseLine(0)).
TakeWhile((LineOfValues) => !LineOfValues.Contains(999));
return lines;
}
}
/// <summary>
/// Extension methods to allow composability of a CSV parser
/// </summary>
public static class ParseExtensions
{
/// <summary>
/// Parsing a string to a nullable integer
/// </summary>
public static int? DefaultParse(this string value)
{
int answer;
int? n = int.TryParse(value, out answer) ? answer : default(int?);
return n;
}
/// <summary>
/// Parsing a string to an integer and returning a default value if parsing fails
/// </summary>
public static int DefaultParse(this string value, int defaultValue)
{
int answer;
int n = int.TryParse(value, out answer) ? answer : defaultValue;
return n;
}
/// <summary>
/// Parsing a single line into an array of ints
/// </summary>
public static IEnumerable<int> ParseLine(this string line, int defaultValue)
{
string[] fields = line.Split(',');
foreach (string field in fields)
yield return (field.DefaultParse(defaultValue));
}
/// <summary>
/// Parsing a single line into an array of nullable ints
/// </summary>
public static IEnumerable<int?> ParseLine(this string line)
{
string[] fields = line.Split(',');
foreach (string field in fields)
yield return (field.DefaultParse());
}
/// <summary>
/// Read text line in successsion
/// </summary>
public static IEnumerable<string> ReadLines(this TextReader reader)
{
string line = string.Empty;
while ((line = reader.ReadLine()) != null)
yield return line;
}
}
/// <summary>
/// Illustrates some of the basic scenarios where extension methods are abuded
/// </summary>
public static class BadExtensionMethods
{
/// <summary>
/// Instance methods cannot be called using a null reference, because the .NET runtime will
/// throw a null reference exception instead of calling your routine. Therefore, never write
/// extension methods designed to work with null objects
/// </summary>
public static int GetStringLength(this string input)
{
if (input == null)
return -1;
else
return input.Length;
}
/// <summary>
/// Do not create extension methods that have significantly worse performance than users expect
/// as it creates a new copy which slows performance
/// </summary>
public static IEnumerable<T> InefficientReverse<T>(this IEnumerable<T> sequence)
{
IList<T> temp = new List<T>(sequence);
return temp.Reverse();
}
// Avoid creating extension methods that have a high likelihood of failure, which often occurs when
// you extend the .NET collection interfaces. Examine this method, which extends ICollection<T>
// by providing support for AddRange(). However, if you use an array to call this routine, the
// compiler throws an exception. Most types that implement ICollection<T> and support Add already
// have their own version of AddRange included. You've added no capabilities but have increased
// the likelihood that someone calling your new extension method will get frustrated by exceptions.
public static void AddRange<T>(this ICollection<T> coll, IEnumerable<T> range)
{
foreach (T item in range)
coll.Add(item);
}
}
}
Note on closures: A closure is a function that can
refer to and alter the values of variables defined in its declaring scope. In C#
closures correspond to anonymous delegates:
int x = 1;
Action<int> Add = new Action<T>(delegate (int y)
{
x = x + y;
}
Here the variable x can be accessed and modified
inside the anonymous delegate. To begin, study
BoundVariables.ModifyBoundVariable() method
below.
The C# compiler converts your queries and lambda expressions into static delegates, or instance delegates, or closures. It chooses which one to create based on the code inside the lambda. Which path the compiler takes depends on the body of the lambda. This has important implications for your code. As a result, modifying the bound variables between queries can introduce errors caused by the interaction of deferred execution and the way the compiler implements closures. Therefore, you should always avoid modifying bound variables that have been captured by a closure.
class Item33
{
public void ShowEffectOfModifyingBoundVariable()
{
BoundVariables bv = new BoundVariables();
bv.ModifyBoundVariable();
}
}
namespace Item33NS
{
class BoundVariables
{
public void ModifyBoundVariable()
{
// Create a sequence of 30 consecutive integers starting at 0, i.e., generate
// 1, 2, ..., 29, 30
int nIndex = 0;
IEnumerable<int> sequence = Utilities.Generate<int>(30, () => nIndex++);
// The following simple assignment creates a very subtle bug: nIndex is modified AFTER
// it has been placed in closure (Generate lambda expression above) but BEFORE the
// query has been executed. This is due to interaction of 'deferred execution' and the
// way the compiler implements closures
nIndex = 20;
// Now iterate and display sequence. Rather the printing 1, 30, the iteration prints
// 20 to 50!!
foreach (int n in sequence)
Trace.WriteLine(n);
Trace.WriteLine("Done");
}
}
/// <summary>
/// Utilities class used in this item
/// </summary>
public static class Utilities
{
/// <summary>
/// Generates a sequence of numbers containing 'count' items using the supplied
/// 'generator' function. Note the use of 'nIndex' variable
/// </summary>
public static IEnumerable<T> Generate<T>(int count, Func<T> generator)
{
int i = 0;
while (i++ < count)
yield return generator();
}
}
/// <summary>
/// This class illustrates the case when a Lambda expression is implemented as a static delegate.
/// Here the body of the lambda expression does not access any instance variables nor local variables.
/// The lambda expression accesses only its parameters. Therefore, the C# compiler creates a static
/// method for the target of the delegate. That's the simplest path the compiler can take. The compiler
/// generates a private static method and corresponding delegate definition whenever the expression to
/// be enclosed can be implemented in a private static method. That includes simple expressions such
/// as the example here or a method that accesses any static class variables
/// </summary>
class LambdaExpressionAsStaticDelegate
{
// Sample method
public void MyFunction()
{
int[] numbers = new int[] { 1, 2, 3, 4, 5 };
IEnumerable<int> sequence = from n in numbers select n * 2;
}
// Method generated by compiler for MyFunction method
public void MyFunction_CompilerVersion()
{
int[] numbers = new int[] { 1, 2, 3, 4, 5 };
if (HiddenDelegateDefinition == null)
HiddenDelegateDefinition = new Func<int, int>(HiddenFunc);
IEnumerable<int> sequence = numbers.Select<int, int>(HiddenDelegateDefinition);
}
private static int HiddenFunc(int n)
{
return (n * n);
}
private static Func<int, int> HiddenDelegateDefinition = null;
}
/// <summary>
/// This class illustates the case when a Lambda expression is implemented as an instance
/// delegate. In this example, the lambda expression requires access to instance variables
/// but not to any local variables.
///
/// Whenever the code inside your lambda expression accesses member variables for your object
/// instances, the compiler generates an instance method representing the code in your lambda
/// expression. There's nothing magical going on here
///
/// </summary>
class LambdaExpressionAsInstanceDelegate
{
private readonly int modulus;
public LambdaExpressionAsInstanceDelegate(int mod)
{
modulus = mod;
}
// Sample method that accesses instance variables
public IEnumerable<int> MyFunction(IEnumerable<int> sequence)
{
return from n in sequence where (n % modulus == 0) select n * n;
}
// Methods generated by compiler for MyFunction method
public IEnumerable<int> MyFunction_CompilerVersion(IEnumerable<int> sequence)
{
return sequence.Where<int>(new Func<int, bool>(this.WhereClause)).Select<int, int>(SelectClause);
}
private bool WhereClause(int n) { return ((n % this.modulus) == 0); }
private static int SelectClause(int n) { return (n * n); }
}
/// <summary>
/// This class illustates the case when a Lambda expression is implemented as a closure
///
/// The compiler does quite a bit more work if any of the code in your lambda expressions
/// accesses local variables or accesses parameters to methods. Here, you need a closure.
/// The compiler generates a private nested class to implement the closure for your local
/// variables. The local variable must be passed to the delegate that implements the body
/// of the lambda expression. In addition, any changes to that local variable performed by
/// the lambda expression must be visible in the outer scope.
///
/// To create the closure, the compiler creates a nested class to implement the behavior you
/// need
/// </summary>
class LambdaExpressionAsClosure
{
private readonly int modulus;
public LambdaExpressionAsClosure(int mod)
{
modulus = mod;
}
// Sample method that accesses instance variables
public IEnumerable<int> MyFunction(IEnumerable<int> sequence)
{
int numValues = 0;
return from n in sequence where (n % modulus == 0) select n * n / ++numValues;
}
// Methods generated by compiler for MyFunction method
public IEnumerable<int> FindValues(IEnumerable<int> sequence)
{
Closure c = new Closure();
c.outer = this;
c.numValues = 0;
return sequence.Where<int>(new Func<int, bool>(this.WhereClause))
.Select<int, int>(new Func<int, int>(c.SelectClause));
}
private bool WhereClause(int n)
{
return ((n % this.modulus) == 0);
}
private sealed class Closure
{
public LambdaExpressionAsClosure outer;
public int numValues;
public int SelectClause(int n)
{
return ((n * n) / ++this.numValues);
}
}
}
}
Many believe that you can't properly develop with anonymous types because they can't be used in more than one method. That's not true. You can write generic methods that use anonymous types. Anonymous types are lightweight types that simply contain read and write properties that usually hold simple values. You will build many of your algorithms around these simple types. You can manipulate anonymous types using lambda expressions and generic methods. Just as you can create private nested classes to limit the scope of types, you can exploit the fact that the scope of an anonymous type is limited to a given method. See TestMethodWithAnonymousType() and TestMethodWithAnonymousType2() methods below.
class Item34
{
public void TestMethodWithAnonymousType()
{
// Define a sequence of numbers
List<int> numbers = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// Query to select even numbers. Returns an anonymous type
var result = from n in numbers where (n % 2 == 0) select new { Number = n };
// The following uses an anonymous type - indicated by 'new {Number = 4 }' statement -
// as an argument to a function
result = from n in FindValue( result, new {Number = 4 }) select n;
}
/// <summary>
///
/// Note that query sequence uses the 'let' clause. Sometimes it is useful to store the result
/// of a sub-expression in order to use it in subsequent expressions. The 'let' keyword creates
/// a new range variable and initializes it with the result of the expression you supply. Once
/// initialized with a value, the range variable cannot be used to store another value. However,
/// if the range variable holds a queryable type, it can be queried.
///
/// Take a look at the 'sequence' query:
/// The query ends in the TakeWhile() method, which has this signature:
/// public static IEnumerable<TSource> TakeWhile<TSource>(this IEnumerable<TSource> source,
/// Func<TSource, bool> predicate);
///
/// Notice that the signature of TakeWhile returns an IEnumerable<T> and has an IEnumerable<T>
/// parameter. In our simple example, T stands in for an anonymous type representing an X,Y
/// pair. Func<T, bool> represents a function that takes a T as its parameter.
///
/// This technique gives you the pathway to creating large libraries and code that works with
/// anonymous types. The following code creates an anonymous type and then processes that type in
/// many generic methods
/// </summary>
public void TestMethodWithAnonymousType2()
{
// Note that TakeWhile operates on an anonymous types
Random randomNumbers = new Random();
var sequence = (from x in Item33NS.Utilities.Generate(100,() => randomNumbers.NextDouble() * 100)
let y = x / 100
select new { x, y }).TakeWhile( point => point.x < 75);
var scaled = from p in sequence select new {x = p.x * 5, y = p.y * 5};
var translated = from p in scaled select new { x = p.x - 20, y = p.y - 20};
var distances = from p in translated
let distance = Math.Sqrt(p.x * p.x + p.y * p.y)
where distance < 500.0
select new { p.x, p.y, distance };
}
// A simple function that returns a list of values that match the given input
private IEnumerable<T> FindValue<T>(IEnumerable<T> input, T value)
{
foreach (T item in input)
if (item .Equals(value))
yield return item;
}
}
Recall from item 28 Augment Minimal Interface Contracts With Extension Methods and item 29 Enhance Constructed Types with Extension Methods that are three main reasons to create extension methods for interfaces or types:
Adding default implementation to interfaces
Creating behaviors on closed generic types, and
Creating composable interfaces.
However, extension methods are not always a good way to express
your designs. In all those cases, you made some enhancements to an existing type
definition, but those enhancements did not fundamentally change the behavior
of the type. Suppose that you write an extension method on some class, say
Person, to producea report of the person on the console. Later you find that you
need to create a report in XML format as well (see namespaces
ConsoleExtensionsBAD and
XMLExtensionsBAD below.)
The problem in these two namespaces is this: Switching a using statement in the
source file changes the format of the report! If a developer uses the wrong
namespace, the program behavior changes. If a developer forgets to use any of
the extension namespaces, the program won't compile. If a developer needs both
of the namespaces in different methods, he/she must split the class definition
into different files, based on which extension method is needed. Using both
namespaces causes a compiler error on an ambiguous reference.
The use of extension methods in these cases is fundamentally wrong: Formatting a Person object for either XML or a console report is not part of the Person type, but instead more closely belongs to the outside environment that uses the Person object.
The key point is this: Extension methods should be used to enhance a type with functionality that naturally extends a type. You should create extension methods only to add functionality that would logically be part of that type. Class PersonReports below illustrates the proper solution to create reports.
// Bad start. Extending classes using extension methods
namespace ConsoleExtensionsBAD
{
public static class ConsoleReport
{
public static string Format(this Person target)
{
return string.Format("{0,20}, {1,15}",
target.LastName, target);
}
}
}
// Even worse. Ambiguous extension methods in different namespaces.
namespace XmlExtensionsBAD
{
public static class XmlReport
{
public static string Format(this Person target)
{
return new XElement("Person",
new XElement("LastName", target.LastName),
new XElement("FirstName", target.FirstName)
).ToString();
}
}
}
// Proper way to create reports rather than using extension methods
public static class PersonReports
{
public static string FormatAsText(Person target)
{
return string.Format("{0,20}, {1,15}", target.LastName, target.FirstName);
}
public static string FormatAsXML(Person target)
{
return new XElement("Person",
new XElement("LastName", target.LastName),
new XElement("FirstName", target.FirstName) ).ToString();
}
}
An expression tree provides a method of translating executable code into data. This can be very valuable if you want to modify or transform code before executing it. In particular, it can be useful if you want to transform C# code such as a LINQ query expression into code that operates on another process, such as a SQL database. Expression trees are not executable code, they are a form of data structure. So how does one translate the raw code found in an expression into an expression tree? See ShowBasicLambdaExpression() method.
Using Visual Studio Expression Tree Visualizer debug tool:
Download ExpressionTreeVisualizer from http://code.msdn.microsoft.com/csharpsamples. Select Downloads tab and download CSharpSamples.zip (English).
Save downloaded file in VS.NET 22008 samples folder. Select Help | Samples menu and click on 'Local Samples Folder'.
Extract samples and build ExpressionTreeVisualizer.
Copy ExpressionTreeVisualizer.dll into My Documents\Visual Studio 2008\Visualizers. See Tools | Options | Projects and Solutions | General for full path.
Run a project that contains an Expression<T>. Set a breakpoint on the expression. Hover your mouse under the variable expression and a small Quick Info box with a magnifying glass will appear. Click on the magnifying class to view the expression tree visualizer.
class ExpressionTreeBasics
{
/// <summary>
/// Consider the following very simple lambda expression:
///
/// Func<int, int, int> function = (a,b) => a + b;
///
/// This statement consists of three sections:
/// 1. A declaration: Func<int, int, int> function
/// 2. An equals operator: =
/// 3. A lambda expression: (a,b) => a + b;
///
/// The variable function points at raw executable code that knows how to add
/// two numbers.
///
/// In the code below, use ExpressionTreeVisualizer to view 'expressionAdd' variable.
/// Note that there are four properties in the Expression<TDelegate> class:
/// 1. Body: Retrieve the body of the expression.
/// 2. Parameters: Retrieve the parameters of the lambda expression.
/// 3. NodeType: Gets the ExpressionType for some node in the tree. This is an
/// enumeration of 45 different values, representing all the different possible
/// types of expression nodes, such as those that return constants, those that
/// return parameters, those that decide whether one value is less than another,
/// etc.
/// 4. Type: Gets the static type of the expression. In this case, the expression
/// is of type Func<int, int, int>.
///
/// Quick Notes on IQueryable<T> and IEnumerable<T>
/// The query expressions found in LINQ to Objects return IEnumerable<T> rather than
/// IQueryable<T>. Why does LINQ to Objects use IEnumerable<T> and LINQ to SQL use
/// IQueryable<T>?
///
/// Here is the declaration for IEnumerable<T>:
/// public interface IEnumerable<T> : IEnumerable
/// {
/// IEnumerator<T> GetEnumerator();
/// }
///
/// As you can see, IEnumerable<T> does not contain a field of type Expression. This points
/// to a fundamental difference between LINQ to Objects and LINQ to SQL. The latter makes
/// heavy use of expression trees, but LINQ to Objects uses them rarely.
///
/// Why are expression trees not a standard part of LINQ to Objects? Consider this simple
/// LINQ to Objects query expression:
///
/// List<int> list = new List<int>() {1,2,3,4,5};
/// var q = from n in list where n < 2 select n;
///
/// We can convert the query expression 'q' directly into .NET code that can be executed.
/// It is not necessary to translate this query into a string that can be passed to another
/// process in order to get the proper answer.
///
/// </summary>
public void ShowBasicLambdaExpression()
{
// A simple delegate that uses Lambda expression
// Note that Func is a delegate that is declared in System namespace as follows:
// public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2);
Func<int, int, int> Add = (int a, int b) => a + b;
int nResult = Add(10, 20);
// The lambda expression used for Add (above) is converted into an expression
// tree declared to be of type Expression<T>. The identifier expression is not
// executable code; it is a data structure called an expression tree.
// Examine expressionAdd with the Expression Tree Visualizer
Expression<Func<int, int, int>> expressionAdd = (int a, int b) => a + b;
// A programmatic way to display some of the information shown in the expression
// tree visualizer
Trace.WriteLine("Param 1: " + expressionAdd.Parameters[0] + ", Param 2: " + expressionAdd.Parameters[1]);
// After converting code into data, we can convert data back into code. The following converts an expression
// tree into executable code
nResult = expressionAdd.Compile()(10, 20);
}
/// <summary>
/// ShowBasicLambdaExpression gave an abstract conceptual understanding of an expression tree.
/// The following examines the key role that an expression tree plays in LINQ, and particularly
/// in LINQ to SQL.
///
/// In the following query, q if of type IQueryable. Here is the declaration of IQueryable:
/// public interface IQueryable : IEnumerable
/// {
/// Type ElementType { get; }
/// Expression Expression { get; }
/// IQueryProvider Provider { get; }
/// }
/// IQueryable.Expression is designed to hold the expression tree associated with an instance of
/// IQueryable. Examine 'q' with the Expression Tree Visualizer and note the complexity of the
/// data structure!
///
/// The main question remains: Why Convert a LINQ to SQL Query Expression into an Expression Tree?
/// A LINQ to SQL query is not executed inside your C# program. Instead, it is translated into SQL,
/// sent across a wire, and executed on a database server. In other words, the query for 'q' is
/// never actually executed inside your program. It is translated into SQL and then exectued on the
/// database server. It is obviously going to be much easier to translate a data structure such as
/// an expression tree into SQL than it is to translate raw IL or executable code into SQL.
///
/// Therefore, expression trees were created in order to make the task of converting code such
/// as a query expression into a string that can be passed to some other process and executed
/// there. It is that simple.
///
/// </summary>
public void TestExpressionTreeInLINQToSQL()
{
// Create a data context
MyDataContext ctxt = new MyDataContext();
//the variable query that is returned by this LINQ expression is of type IQueryable
//var q = from t in ctxt.tblFuturesTrades where t.Status == "Pending" select t;
}
}
LINQ is built on two concepts: a query language, and a translation from that query language into a set of methods. The C# compiler converts query expressions written in that query language into method calls. For example, a where clause translates to a call to a method named Where(), with the proper set of parameters. Therefore, every query expression has a mapping to a method call or calls.
The full query expression pattern contains eleven methods:
Where, Select,
SelectMany, Join,
GroupJoin, OrderBy,
OrderByDescending, GroupBy<K>,
GroupBy<K,E>, ThenBy,
and ThenByDescending.
The .NET Framework provides two general-purpose reference implementations of
this pattern:
System.Linq.Enumerable: The methods
in this class provide an implementation of the standard query
operators for querying data sources that implement
IEnumerable<T>. This means that any object that implements
IEnumerable<T> will have available a set of
extension methods whose implementation is found in
System.Linq.Enumerable.
System.Linq.Queryable: The methods in this class provide an implementation of the standard query operators for querying data sources that implement IQueryable<T>. This means that any object that implements IQueryable<T> will have available a set of extension methods whose implementation is found in System.Linq.Queryable. IQueryable<T> interface is intended for implementation by query providers.
Therefore, System.Linq.Enumerable provides extension methods for IEnumerable<T> and System.Linq.Queryable provides extension methods for IQuerable<T>. IEnumerable<T> is used for LINQ to Objects, whereas IQueryable<T> is designed for LINQ providers which need to translate expression trees into other forms - web service calls, SQL, LDAP etc.
class Item36
{
/// <summary>
/// Displays few basic LINQ select operations
/// </summary>
public void TestLINQBasics()
{
// Create a list of numbers
int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// The following three statements are equivalent
// The expression 'from n in numbers' binds the range variable n to each value
// in numbers. The where clause define a filter whose output is a proper subset
// of the input sequence containing only those elements that satisfy the predicate.
// Finally, the select is used to transform one input element to a different
// element or to a different type. In the 2nd statement below 'select' is
// redundant and can be removed. Note that a proper select method must produce
// exactly one output element for each input element. Also, a proper implementation
// of Select must not modify the items in the input sequence.
var first5numbers_v1 = from n in numbers where n < 5 select n;
var first5numbers_v2 = numbers.Where((int n) => n < 5).Select((int m) => m);
var first5numbers_v3 = numbers.Where((int n) => n < 5);
// Select is used to transform or project one input element into a different element
var squares1 = from n in numbers select n * n;
foreach (var v in squares1) Trace.WriteLine(v);
// Select is used to transform or project one input element into a different type
var squares2 = from n in numbers select new {Number = n, Square = n^2};
foreach (var v in squares2) Trace.WriteLine("Number: " + v.Number + ", Square: " + v.Square);
// Same as squares2
var squares3 = numbers.Select((int n) => new { Number = n, Square = n ^ 2 });
}
/// <summary>
/// Explores ordering by queries
/// </summary>
public void TestLinqOrdering()
{
// Create an array of employees (note syntax for using anonymous types)
var Employees = new[] { new {Age=20, FirstName="Y", LastName="G"},
new {Age=30, FirstName="A", LastName="A" },
new {Age=30, FirstName="Z", LastName="W" },
new {Age=30, FirstName="B", LastName="A" },
new {Age=40, FirstName="N", LastName="M" },
};
// Ordering relations map to the OrderBy and ThenBy methods. The following two
// statements, p1 and p2, are equivalent. Notice in the definition of the query
// expression pattern that ThenBy operates on a sequence returned by OrderBy or
// ThenBy. The output from both is:
// Age: 40, LastName: M, FirstName: N
// Age: 30, LastName: A, FirstName: A
// Age: 30, LastName: A, FirstName: B
// Age: 30, LastName: W, FirstName: Z
{
var p1 = from e in Employees
where e.Age >= 30
orderby e.Age descending, e.LastName, e.FirstName
select e;
var p2 = Employees.Select(e => e).
Where(e => e.Age >= 30).
OrderByDescending(e => e.Age).ThenBy(e => e.LastName).ThenBy(e => e.FirstName);
foreach (var p in p1) Trace.WriteLine("Age: " + p.Age + ", LastName: " + p.LastName + ", FirstName: " + p.FirstName);
foreach (var p in p2) Trace.WriteLine("Age: " + p.Age + ", LastName: " + p.LastName + ", FirstName: " + p.FirstName);
}
// This transformation is not the same if the orderby clauses are expressed as
// different clauses. The following query sorts the sequence entirely by LastName,
// then sorts the entire sequence again by FirstName, and then sorts again by Age.
// The output from both is:
// Age: 30, LastName: A, FirstName: A
// Age: 30, LastName: A, FirstName: B
// Age: 40, LastName: M, FirstName: N
// Age: 30, LastName: W, FirstName: Z
{
var p1 = from e in Employees
where e.Age >= 30
orderby e.Age
orderby e.LastName
orderby e.FirstName
select e;
var p2 = Employees.Select(e => e).
Where(e => e.Age >= 30).
OrderBy(e => e.Age).OrderBy(e => e.LastName).OrderBy(e => e.FirstName);
foreach (var p in p1) Trace.WriteLine("Age: " + p.Age + ", LastName: " + p.LastName + ", FirstName: " + p.FirstName);
foreach (var p in p2) Trace.WriteLine("Age: " + p.Age + ", LastName: " + p.LastName + ", FirstName: " + p.FirstName);
}
}
/// <summary>
/// The following investigates expression translations that involve multiple steps.
/// Those queries involve either groupings or multiple 'from' clauses that introduce
/// continuations. Query expressions that contain continuations are translated into
/// nested queries. Then those nested queries are translated into methods.
/// </summary>
public void TestNestedQueries()
{
// Create an array of employees. Note the use of anonymous types
var Employees = new[] { new {Age=20, FirstName="Y", LastName="G"},
new {Age=30, FirstName="A", LastName="A" },
new {Age=30, FirstName="Z", LastName="W" },
new {Age=30, FirstName="B", LastName="A" },
new {Age=40, FirstName="N", LastName="M" }};
// The following queries are all identical. r1 is the original query.
// Before any other translations are performed, the continuation is
// translated into a nested query (r2). Once the nested query is created,
// the methods translate into r3 query
// Each of the following queries gives the following output
// Age: 20, Size: 1
// Age: 30, Size: 3
// Age: 40, Size: 1
{
var r1 = from e in Employees
group e by e.Age into agegroups
select new { Age = agegroups.Key, Size = agegroups.Count() };
var r2 = from agegroups in
from e in Employees group e by e.Age
select new { Age = agegroups.Key, Size = agegroups.Count() };
var r3 = Employees.GroupBy(e => e.Age).Select(agegroups => new { Age = agegroups.Key, Size = agegroups.Count() });
foreach (var r in r1) Trace.WriteLine("Age: " + r.Age + ", Size: " + r.Size);
foreach (var r in r2) Trace.WriteLine("Age: " + r.Age + ", Size: " + r.Size);
foreach (var r in r3) Trace.WriteLine("Age: " + r.Age + ", Size: " + r.Size);
}
// The foregoing query shows a GroupBy that returns a single sequence. The other
// GroupBy method in the query expression pattern returns a sequence of groups
// in which each group contains a key and a list of values.
// GroupBy methods produce a sequence of key/value list pairs; the keys are the
// group selectors, and the values are the sequence of items in the group
// Output from both is:
// Age: 20
// Employee Age: 20, FirtName: Y, LastName: G
// Age: 30
// Employee Age: 30, FirtName: A, LastName: A
// Employee Age: 30, FirtName: Z, LastName: W
// Employee Age: 30, FirtName: B, LastName: A
// Age: 40
// Employee Age: 40, FirtName: N, LastName: M
{
var r1 = from e in Employees
group e by e.Age into agegroups
select new { Age = agegroups.Key, Employees = agegroups.AsEnumerable() };
var r2 = Employees.GroupBy(e => e.Age).Select(agegroups => new { Age = agegroups.Key, Employees = agegroups.AsEnumerable() });
foreach (var r in r1)
{
Trace.WriteLine("Age: " + r.Age);
foreach (var e in r.Employees)
Trace.WriteLine("Employee Age: " + e.Age + ", FirtName: " + e.FirstName + ", LastName: " + e.LastName);
}
foreach (var r in r2)
{
Trace.WriteLine("Age: " + r.Age);
foreach (var e in r.Employees)
Trace.WriteLine("Employee Age: " + e.Age + ", FirtName: " + e.FirstName + ", LastName: " + e.LastName);
}
}
}
/// <summary>
/// SelectMany performs a cross join on the two source sequences
/// </summary>
///
public class ParentDetails
{
public string ParentName { get; set; }
public List<string> Children { get; set; }
}
public void TestSelectMany()
{
// Create a collection of parent and their respective children
ParentDetails[] details = { new ParentDetails { ParentName="Mr. A", Children = new List<string>(){"X", "Y"}},
new ParentDetails { ParentName="Mr. B", Children = new List<string>(){"G", "J"}},
new ParentDetails { ParentName="Mr. C", Children = new List<string>(){"J", "R", "K"}} };
// Query using SelectMany
var q1_SelectMany = details.SelectMany((pd) => pd.Children);
// Only one foreach statement is required since the result is one-dimensional
// Output is:
// X
// Y
// G
// J
// J
// R
// K
foreach (string child in q1_SelectMany)
Trace.WriteLine(child);
// Query using Select
var q1_Select = details.Select((pd) => pd.Children);
// Two foreach statements are required because q2 returns a collection of arrays#
// Output is:
// X
// Y
// G
// J
// J
// R
// K
foreach (List<string> children in q1_Select)
foreach (string child in children)
Trace.WriteLine(child);
}
}
When you define a query, you do not actually get the data and
populate a sequence. You are actually defining only the steps that will be
executed when you choose to iterate that query. This means that each
new enumeration produces new results. This is called lazy
evaluation. Lazy evaluation is not always what you want. The other
option is eager evaluation where you retrieve the results once and
retrieve them now.
Every time you write a query be enumerated more than once, you need to consider
which behavior you want. Do you want a snapshot of your data (eager evaluation),
or do you want to create a description of the code you will execute in order to
create the sequence of values (lazy evaluation)?
Some query expressions must retrieve the entire sequence before
they can proceed to create their answer. This means that Query expressions may
operate on infinite sequences! See TestInfiniteSequences for an example.
There are a number of query operators that must have the entire sequence in
order to operate correctly. where uses the
entire sequence. orderby needs the entire
sequence to be present. max and
min need the entire sequence. There's no way to
perform these operations without examining every element in the sequence. When
you need these capabilities, you'll use these methods.
You also need to think about the consequences of using methods that require access to the entire sequence. As you've seen, you need to avoid any methods that require the entire sequence if the sequence might be infinite. Second, even if the sequence is not infinite, any query methods that filter the sequence should be front-loaded in the query. If the first steps in your query remove some of the elements from the collection, that will have a positive effect on the performance of the rest of the query. For example, queries that filter before ordering may have better performance than queries that order and then filter
The discussion so far has been on using lazy evaluation. In most cases, that's the best approach. At other times, though, you do want a snapshot of the values taken at a point in time. There are two methods you can use to generate the sequence immediately and store the results in a container: ToList() and ToArray(). Both methods perform the query and store the results in a List<T> or an Array, respectively. By forcing the query to execute immediately, these methods capture a snapshot of the data right now.
class Item37
{
/// <summary>
/// In this example of lazy evaluation, the sequence variable 'dates' does not hold
/// the elements created. Rather, it holds the expression tree that can create the
/// sequence. Notice that the sequence is generated each time it is iterated, as
/// evidenced by the different time stamps.
///
/// </summary>
public void TestLazyEvaluation()
{
// Define the query to get time (does not execute the query!)
IEnumerable<DateTime> dates = Generate<DateTime>(5, () => (DateTime.Now));
// The following iteration executes the query! Note the output
// 12:20:25.333509
// 12:20:25.358898
// 12:20:25.358898
// 12:20:25.359875
// 12:20:25.360851
foreach (DateTime date in dates)
Trace.WriteLine(date.ToString("hh:mm:ss.FFFFFF"));
// The following iteration executes the query! Note that the output is differnet
// from the first iteration above
// 12:20:25.360851
// 12:20:25.361828
// 12:20:25.361828
// 12:20:25.362804
// 12:20:25.363781
foreach (DateTime date in dates)
Trace.WriteLine(date.ToString("hh:mm:ss.FFFFFF"));
}
/// <summary>
/// You can use lazy evaluation to compose queries from existing queries. Instead of
/// retrieving the results from one query and then processing them as a separate step,
/// you can compose queries in different steps and then execute the composed query only
/// once.
///
/// In the example below, q1 and q2 share functional composition, not data. q2 is not
/// built by enumerating the values in q1 and modifying each value. Rather, it is created
/// by executing the code that produces q1, followed by the code that produces q2. If you
/// iterate the two queries at different times, you'll see unrelated sequences. q2 will
/// not contain the converted values from q1. Instead, it will contain totally new values.
/// </summary>
public void TestFunctionalCompositionInLazyEvaluation()
{
int nStartValue = 0;
var q1 = Generate(5, () => nStartValue++);
var q2 = from value in q1 select value * 10;
// The following iteration executes the functional composition of q1 and q2
// The following values are produced
// 0
// 10
// 20
// 30
// 40
foreach (int nValue in q2)
Trace.WriteLine(nValue);
}
/// <summary>
/// The 'numbers' sequences only generates 5 numbers even though Generate() can generate
/// up to int.MaxValue numbers.
///
/// However, the 'numbers2' sequence will run forever. It runs forever because the query
/// must examine every single number to determine which methods match. This version of
/// the same logic requires the entire sequence!
/// </summary>
public void TestInfiniteSequences()
{
// Generate only 5 numbers
int nStartNumber = 0;
IEnumerable<int> numbers = Generate(int.MaxValue, () => (nStartNumber++)).Take(5);
// Iterate results
foreach (int nValue in numbers)
Trace.WriteLine(nValue);
nStartNumber = 0;
IEnumerable<int> numbers2 = Generate(int.MaxValue, () => (nStartNumber++)).Where( (nValue) => (nValue < 5));
// -------------> RUNS FOREVER <-----------
foreach (int nValue in numbers2)
Trace.WriteLine(nValue);
}
/// <summary>
/// Generates a sequence of values. nCount determines the number of values to
/// generate and generator is a delegate that generates actual values
/// </summary>
private IEnumerable<TResult> Generate<TResult>(int nCount, Func<TResult> generator)
{
for (int i = 0; i < nCount; i++)
yield return generator();
}
}
When you create code that executes over a sequence of values and the code throws an exception somewhere in that sequence processing, you'll have problems recovering state. You don't know how many elements were processed, if any. You don't know what needs to be rolled back. You can't restore the program state at all. See TestExceptionInAction() method below.
Note that not every method exhibits this problem. Many methods examine a sequence but do not modify it. We would not have this problem if we used another action, say DisplaySalary, that would print the salary without modifying it. You fix the situation illustrated by UpdateSalary method below by guaranteeing that whenever the method does not complete, the observable program state does not change. You can implement this in various ways:
If approach 1 is not possible, i.e. you could not avoid the possibility of an exception, rework the algorithm by doing all the work on a copy and then replacing the original sequence with the copy only if the operation completes successfully. See method TestExceptionInAction2() below. You can see the cost of those changes here. First, there's quite a bit more code than in the earlier versions. We've also changed the performance metrics for the application. This newer version creates a second copy of all the employee records and then swaps the reference to the new list of employees with the reference to the old list. If the employee list is large, that could cause a big performance bottleneck.
Also note that this new version limits your ability to compose operations using multiple functions. This code snippet caches the full list. This means that its modifications aren't composed along with other transformations in a single enumeration of the list. Each transformation becomes an imperative operation. In practice, you can work around this issue by creating one query statement that performs all the transformations. You cache the list and swap the entire sequence as one final step for all the transformations. Using that technique, you preserve the composability and still provide the strong exception guarantee.
class Item39
{
public void TestExceptionInAction()
{
// Create a list of Persons
List<MyEmployee> lstEmp = new List<MyEmployee>(){ new MyEmployee("X", 2000.0),
new MyEmployee("Y", 2000.0),
new MyEmployee("Z", 2000.0)};
try
{
// Update salary for all employees
Action<MyEmployee> action = UpdateSalary;
lstEmp.ForEach(action);
}
catch (Exception ex)
{
// Enumerate over all employees to show current state. Note some employees
// have their salary incremented, but not others. Output:
// X/4000
// Y/2000
// Z/2000
foreach (MyEmployee emp in lstEmp)
Trace.WriteLine(emp.Name + "/" + emp.Salary);
}
}
public void TestExceptionInAction2()
{
// Create a list of Persons
List<MyEmployee> lstEmp = new List<MyEmployee>(){ new MyEmployee("X", 2000.0),
new MyEmployee("Y", 2000.0),
new MyEmployee("Z", 2000.0)};
try
{
// Create a copy of the original list
List<MyEmployee> lstEmpCopy= new List<MyEmployee>();
foreach (MyEmployee emp in lstEmp)
lstEmpCopy.Add(new MyEmployee(emp.Name, emp.Salary));
// Update salary for all employees ON A COPY of the original list
Action<MyEmployee> action = UpdateSalary;
lstEmpCopy.ForEach(action);
// No exception. Now we can copy the altered list into the original list
lstEmp.Clear();
lstEmp.AddRange(lstEmpCopy);
}
catch (Exception ex)
{
// Failed to updated list of employees. State of the original list is maintained
}
// Show the state of the original list
foreach (MyEmployee emp in lstEmp)
Trace.WriteLine(emp.Name + "/" + emp.Salary);
}
/// <summary>
/// Helper Action<T> method to update salaries. Throws an exception to illustrate effect of
/// throwing exceptions in an Action<T> function
/// </summary>
private void UpdateSalary(MyEmployee emp)
{
// Update salary for the given employee
emp.Salary = emp.Salary * 2.0;
// Simulate an exception
throw new ApplicationException("Employee " + emp.Name + " is no longer on the pay roll");
}
private void UpdateSalaryFix1(MyEmployee emp)
{
// Update salary for the given employee, only if the employee is on the pay roll
if (IsEmployeeOnPayRoll(emp))
emp.Salary = emp.Salary * 2.0;
}
private bool IsEmployeeOnPayRoll(MyEmployee emp) { return true; }
}
namespace Item39NS
{
public class MyEmployee
{
public string Name { get; set; }
public double Salary { get; set; }
public MyEmployee(string name, double salary)
{
Name = name;
Salary = salary;
}
}
}
Will the following two lines of code behave the same way?
object o = Foo( Method1(), Method2(), Method3() );
object o = Foo( () => Method(), () => Method2(), () => Method3() );
At runtime, first line of code calls Method1()
to generate first parameter to Foo, then calls
Method2() to generate second parameter, then calls
Method3() to generate third and last parameter. It
then calls Foo with the three generated parameters.
This is Imperative code'; it details step-by-step instructions
that explain how something gets done.
As for the second line, it does the following at runtime:
It calls Foo(), passing the lambda expressions that could call Method1, Method2, and Method3.
Inside Foo, if and only if the result of Method1 is needed, Method1 is called.
Inside Foo, if and only if the result of Method2 is needed, Method2 is called.
Inside Foo, if and only if the result of Method3 is needed, Method3 is called.
Method1, Method2, and Method3 may be called in any order, as many times (including zero) as needed.
The second line of code illustrates Declarative code; the declarative model may or may not execute all or any of the methods. The declarative version may execute any of the methods more than once. This is the difference between (1) calling a method and passing the results to a method and (2) passing a delegate to the method and letting the method call the delegate. You may get different results from different runs of the application, depending on what actions take place in these methods.
If data and methods are interchangeable, which should you choose? And, more importantly, when should you choose which? The most important difference is that data must be preevaluated, whereas a method can be lazy-evaluated. When you must evaluate data early, you must preevaluate the method and use the result as the data, rather than take a functional approach and substitute the method.
Recall that a closure is a function that can refer to and alter the values of variables defined in its declaring scope. In C# closures correspond to anonymous delegates:
Person person = new Person("Yazan");
Action<int> Add = new Action<T>(delegate (int y)
{
person.PrintDetails();
}
Here the variable person can be
accessed and modified inside the anonymous delegate. The main point is this:
When you capture a variable in a closure (person
in example above), the object referenced by that variable does not go out of
scope until the last delegate referencing that captured variable goes out of
scope. The implication is that you really don't know when local
variables go out of scope if you return something that is represented by a
delegate using a captured variable. To further explain this point, consider this
construct:
int counter = 0;
IEnumerable<int> numbers = Extensions.Generate(30, () => counter++);
It generates code that looks something like this:
private class Closure
{
public int generatedCounter;
public int generatorFunc()
{
return generatedCounter ++;
}
}
// usage
Closure c = new Closure();
c.generatedCounter = 0;
IEnumerable<int> sequence = Extensions.Generate(30, new Func<int>(c.generatorFunc));
The hidden nested class members have been bound to delegates used by
Extensions.Generate. That can affect the lifetime of
the hidden object and therefore can affect when any of the members are eligible
for garbage collection. In other words, the local variable
c lives beyond the end of the method. Problems arise when some variables
hold on to expensive resources. They represent types that implement
IDisposable and need to be explicitly cleaned up.
These points are illustrated in
CaptruingExpensiveResouces() method.
class Item41
{
private const string FILEPATH = @"D:\Projects\MoreEffectiveCS\MoreEffectiveCS\C#3\Item31Input.txt";
public void CaptruingExpensiveResouces()
{
/* Incorrect way of using ReadCSVFile. Does not close the file */
{
TextReader t = new StreamReader(FILEPATH);
IEnumerable<string[]> lines = ReadCSVFile( t );
DisplayLines( lines );
}
/* Incorrect way of using ReadCSVFile. Closes file before reading it */
try
{
IEnumerable<string[]> lines = null;
using (TextReader t = new StreamReader(FILEPATH))
{
lines = ReadCSVFile( t );
} // Files closed here. Stream has not beed read yet due to 'deferred execution'
// File read during iteration. However file is close, hence exception
// "Cannot read from a closed TextReader"
DisplayLines( lines );
}
catch (Exception ex)
{
Trace.WriteLine( ex.Message );
}
/* Correct way of using ReadCSVFile */
try
{
using (TextReader t = new StreamReader(FILEPATH))
{
IEnumerable<string[]> lines = ReadCSVFile(t);
// File read during iteration
DisplayLines(lines);
} // Files closed here AFTER it has been read
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
}
/* Last block of code works, but not all problems are that simple. This will lead
to lots of duplicated code.*/
try
{
IEnumerable<string> lines = ReadLines2(FILEPATH); ;
IEnumerable<string[]> parsedLines = ReadCSVFile2(lines);
DisplayLines(parsedLines);
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
}
}
/// <summary>
/// Helper method return lines from a CSV file
/// </summary>
private IEnumerable<string[]> ReadCSVFile(TextReader reader)
{
var lines = from line in reader.ReadLines() select line.Split( new char[] {','} );
return lines;
}
/// <summary>
/// Displays data for debugging purposes
/// </summary>
/// <param name="lines"></param>
private void DisplayLines( IEnumerable<string[]> lines )
{
foreach (var line in lines)
Trace.WriteLine( string.Join( ",", line) );
}
private IEnumerable<string[]> ReadCSVFile2(IEnumerable<string> lines)
{
var parsedLines = from line in lines select line.Split(new char[] { ',' });
return parsedLines;
}
public static IEnumerable<string> ReadLines2(string path)
{
using (StreamReader reader = new StreamReader(path))
{
string line = reader.ReadLine();
while (line != null)
{
yield return line;
line = reader.ReadLine();
}
}
}
}
namespace Item41NS
{
/// <summary>
/// Extends TextReader
/// </summary>
internal static class TextReaderExtensions
{
public static IEnumerable<string> ReadLines(this TextReader reader)
{
string line = reader.ReadLine();
while (line != null)
{
yield return line;
line = reader.ReadLine();
}
}
}
}
IQueryable<T> provides functionality to
evaluate queries against a specific data source (typically SQL Server database)
wherein the type of the data is not specified. The
IQueryable interface inherits the IEnumerable
interface so that if it represents a query, the results of that query can be
enumerated. Enumeration causes the expression tree associated with an
IQueryable object to be executed. The definition of
executing an expression tree is specific to a query provider. For
example, for a SQL Server database, executing an expression tree involves
translating the expression tree to SQL and then executing this SQL against the
database.
IEnumerable<T> on the other hand, exposes an
enumerator, which supports a simple iteration over a collection of a specified
type.
IQueryable<T> and IEnumerable<T>
have very similar API signatures. IQueryable<T>
derives from IEnumerable<T>. However, Their
behaviours are different and their performance metrics can be very different.
class Item42
{
/// <summary>
/// The following two queries q1 and q2 return the same result, but they do their work
/// in very different ways: The first query uses the normal LINQ to SQL version that is
/// built on IQueryable functionality. The second version forces the database objects
/// into IEnumerable sequences and does MORE OF ITS WORK LOCALLY.
///
/// When the results of the first query are executed (answer1), the LINQ to SQL libraries
/// compose the results from ALL the query statements. In the example below, this means that
/// one call is made to the database. It also means that one SQL query performs both the
/// where clause AND the orderby clause.
///
/// In the second case, returning the first query as an IEnumerable<T> sequence means that
/// subsequent operations use the LINQ to Objects implementation and are executed using
/// delegates. The first statement causes a call to the database to retrieve all male employees.
/// The second orders the set returned by the first call by employee Id. That sort operation
/// occurs locally.
///
/// The processing is different at every step of the way. Enumerable<T> extension methods
/// use delegates for the lambda expressions as well as the function parameters whenever they
/// appear in query expressions. Queryable<T>, on the other hand, uses expression trees to
/// process those same function elements. An expression tree is a data structure that holds
/// all the logic that makes up the actions in the query. The Enumerable<T> version must execute
/// locally. The lambda expressions have been compiled into methods, and they must execute now
/// on the local machine. This means that you need to pull all the data into the local application
/// space from wherever it resides. You'll transfer much more data, and you'll throw away whatever
/// isn't necessary. In contrast, the Queryable version parses the expression tree. After examining
/// the expression tree, this version translates that logic into a format appropriate for the
/// provider and then executes that logic where it is closest to the data location. The result
/// is much less data transfer and better overall system performance.
/// </summary>
public void TestIQueryableAndIEnumerable()
{
// Create a context to the AdvanetureWorks database
AdventureWorksLINQDataContext ctxt = new AdventureWorksLINQDataContext();
// IQueryable query. Generates the following SQL (note the inclusion of order by clause)
// exec sp_executesql N'SELECT [t0].[EmployeeID], [t0].[BirthDate]
// FROM [HumanResources].[Employee] AS [t0]
// WHERE UNICODE([t0].[Gender]) = @p0
// ORDER BY [t0].[EmployeeID]',N'@p0 int',@p0=77
// Also note the use of q1 and answer1, implicitly typed local variables (See Item 30)
var q1 = from e in ctxt.Employees where e.Gender == 'M' select new { e.EmployeeID, e.BirthDate };
var answer1 = from e in q1 orderby e.EmployeeID select e;
foreach (var e in answer1)
Trace.WriteLine(e.EmployeeID + "/" + e.BirthDate);
// IEnumerable query. Generates the following SQL (note the absence of an order by clause)
// exec sp_executesql N'SELECT [t0].[EmployeeID], [t0].[BirthDate]
// FROM [HumanResources].[Employee] AS [t0]
// WHERE UNICODE([t0].[Gender]) = @p0',N'@p0 int',@p0=77
var q2 = (from e in ctxt.Employees where e.Gender == 'M' select new { e.EmployeeID, e.BirthDate }).AsEnumerable();
var answer2 = from e in q2 orderby e.EmployeeID select e;
foreach (var e in answer2)
Trace.WriteLine(e.EmployeeID + "/" + e.BirthDate);
}
/// <summary>
/// IQueryable providers don't parse any arbitrary method. Instead, they understand a set of operators,
/// and possibly a defined set of methods, that are implemented in the .NET Framework. If your queries
/// contain other method calls (see q2 below), you may need to force the query to use the Enumerable
/// implementation.
///
/// For the querys below (q1 and q2) the first query works, because LINQ to Objects uses delegates to
/// implement queries as method calls. The AsEnumerable() call forces the query into the local client
/// space, and the where clause executes using LINQ to Objects. The second query throws an exception.
/// The reason is that LINQ to SQL uses an IQueryable<T> implementation. LINQ to SQL contains an
/// IQueryProvider that translates your queries into T-SQL. That T-SQL then gets remoted to the database
/// engine, which executes the SQL statements in that context. q2 query attempts to include IsSales
/// in the generated T-SQL, but IsSales is unknown to T-SQL, and therefore throws an exception.
///
/// In a typical tradeoff of performance versus robustness, you can avoid the exception by translating
/// the query result explicitly to an IEnumerable<T>. The downside of that solution is that the LINQ
/// to SQL engine now returns the entire set of dbContext.Products from the database. Furthermore, the
/// remainder of the query is executed locally. Because IQueryable<T> inherits from IEnumerable<T>,
/// this method can be called using either source.
/// </summary>
public void TestArbitraryMethodsInIQueryable()
{
try
{
// Create a context to the AdvanetureWorks database
AdventureWorksLINQDataContext ctxt = new AdventureWorksLINQDataContext();
// Works as expected
var q1 = from e in ctxt.Employees.AsEnumerable() where IsSales(e) select new { e.EmployeeID, e.Title };
foreach (var e in q1)
Trace.WriteLine("Employee ID: " + e.EmployeeID);
// Throws the following System.NotSupportedException:
// "Method 'IsSales(MoreEffectiveCS.Employee)' has no supported translation to SQL."
var q2 = from e in ctxt.Employees where IsSales(e) select new { e.EmployeeID, e.Title };
foreach (var e in q2)
Trace.WriteLine("Employee ID: " + e.EmployeeID);
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
}
}
private bool IsSales(Employee e)
{
return e.Title.Contains("Sales");
}
/// <summary>
/// AsQueryable() looks at the runtime type of the sequence. If the sequence is an IQueryable,
/// it returns the sequence as an IQueryable. In contrast, if the runtime type of the sequence
/// is an IEnumerable, AsQueryable() creates a wrapper that implements IQueryable using the
/// LINQ to Objects implementation, and it returns that wrapper. You get the Enumerable
/// implementation, but it's wrapped in an IQueryable reference.
///
/// Using AsQueryable() gives you the maximum benefit. Sequences that already implement IQueryable
/// will use that implementation, and sequences that support only IEnumerable will still work. When
/// client code hands you an IQueryable sequence, your code will properly use the Queryable<T> methods
/// and will support expression trees and foreign execution. And if you are working with a sequence
/// that supports only IEnumerable<T>, then the runtime implementation will use the IEnumerable
/// implementation
/// </summary>
public void TestAsQueryable()
{
// Create a context to the AdvanetureWorks database
AdventureWorksLINQDataContext ctxt = new AdventureWorksLINQDataContext();
// The following query maps to:
// exec sp_executesql N'SELECT [t0].[EmployeeID], [t0].[Title]
// FROM [HumanResources].[Employee] AS [t0]
// WHERE [t0].[Title] LIKE @p0',N'@p0 nvarchar(7)',@p0=N'%Sales%'
var q = from e in ctxt.Employees.AsQueryable()
where e.Title.Contains("Sales") select new { e.EmployeeID, e.Title };
foreach (var e in q)
Trace.WriteLine("Employee ID: " + e.EmployeeID);
}
}
When you write a query that is supposed to return exactly one
element, you should use Single().
Single() returns exactly one element. If no elements
exist, or if multiple elements exist, then Single()
throws an exception. The exception helps you make a quick diagnosis and correct
the problem. Note that Single() immediately
evaluates the query and either returns the single element, or throws an
exception.
If your query can return zero or one element, you can use
SingleOrDefault(). However, remember that
SingleOrDefault() still throws an exception when more than one value is
returned. You are still expecting no more than one value returned from your
query expression.
There are times when you expect to get more than one value but you want a
specific one. The best choice is First() or
FirstOrDefault(). Both methods return the first
element in the returned sequence. If the sequence is empty, the default is
returned.
class Item43
{
public void TestSingle()
{
// Create a list of Person objects
var persons = new List<Person>{ new Person{FirstName="A", LastName="X", Salary=1000},
new Person{FirstName="A", LastName="Y", Salary=2000},
new Person{FirstName="A", LastName="Z", Salary=3000} };
try
{
// Throws an exception. More than one object whose FiratName is "A"
var person = (from p in persons where p.FirstName == "A" select p).Single();
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message); // ERROR: "Sequence contains more than one element"
}
try
{
// Throws an exception. There is no object whose FiratName is "A". Should have
// used SingleOrDefault(). See TestSingleOrDefault()
var person = (from p in persons where p.FirstName == "B" select p).Single();
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message); // ERROR: "Sequence contains no elements"
}
}
public void TestSingleOrDefault()
{
// Create a list of Person objects
var persons = new List<Person>{ new Person{FirstName="A", LastName="X", Salary=1000},
new Person{FirstName="A", LastName="Y", Salary=2000},
new Person{FirstName="A", LastName="Z", Salary=3000} };
try
{
// Throws an exception. More than one object whose FiratName is "A"
var person = (from p in persons where p.FirstName == "A" select p).SingleOrDefault();
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message); // ERROR: "Sequence contains more than one element"
}
try
{
// Does NOT throw an exception. The query returns NULL
var person = (from p in persons where p.FirstName == "B" select p).SingleOrDefault();
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
}
}
public void TestFirstOrDefault()
{
// Create a list of Person object
var persons = new List<Person>{ new Person{FirstName="A", LastName="X", Salary=1000},
new Person{FirstName="A", LastName="Y", Salary=2000},
new Person{FirstName="A", LastName="Z", Salary=3000} };
try
{
// Get the person with the highest salary
var HighestPaid = (from p in persons orderby p.Salary descending select p).First();
// Get the person with the second highest salary. Note that we use First() rather than Take()
// to emphasize that we wanted exactly one element, and not a sequence containing one element
var SecondHighestPaid = (from p in persons orderby p.Salary descending select p).Skip(1).First();
// Returns null
var Paid25000 = (from p in persons where p.Salary == 2500 select p).FirstOrDefault();
// Throws an exception: "Sequence contains no elements"
var Paid35000 = (from p in persons where p.Salary == 3500 select p).First();
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
}
}
}
namespace Item43NS
{
internal class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public double Salary { get; set; }
}
}
Nullable types require more checks than non-nullable types. It
makes more sense to use non-nullable structs whenever you can and to limit
nullable types to those algorithms that require the nullable abstraction.
Programming against nullable types is more complicated than programming against
the corresponding non-nullable value. Nullables mean extra checking: What should
happen when the value is missing? The answer will vary, but for every algorithm,
the missing value should be interpreted in some known way. Your goal should be
to isolate the extra work required for nullable values to the smallest set of
code.
TestNullableSemantics() method below
illustrates effect applying comparison and numeric operations to nullable types.
TestNullableSerialization()
illustrates some of the issues encountered when serializing types that contain
nullables. Those member elements appear to be typed as value types, and yet the
value itself is null. Tools that build classes from XML documents create
ints, rather than nullable ints.
Deserialization using those tools would produce the same errors when your
program encounters null values.
Nullable types also introduce complicated behavior when you work with virtual
methods defined in System.Object or with interfaces
implemented by the underlying value type. Those operations will cause a boxing
conversion of the enclosed value type to System.Object
or to the interface pointer. See
TestNullableBoxing() and
TestNullableBoxing2() methods.
class Item45
{
/// <summary>
/// Nullable types add semantics to everyday operations on value types:
/// * Any comparison involving a NULL returns FALSE.
/// * Nullables support equality
/// * Any numeric operation involving a nullable numeric type with no value results in a
/// nullable type with no value
/// * The fact that any null in a numeric expression causes the result of that expression
/// to be null, means that you often must define default values for those operations.
/// This is easily accomplished using the coalescing operator (??).
///
/// </summary>
public void TestNullableSemantics()
{
int? n1 = 0;
int? n2 = default(int?); // n2 is null
int? n3 = default(int?); // n3 is null
// Comparisons
Trace.WriteLine("n1 < n2: " + (n1 < n2)); // n1 < n2: False
Trace.WriteLine("n1 > n2: " + (n1 > n2)); // n1 > n2: False
Trace.WriteLine("n1 == n2: " + (n1 == n2)); // n1 == n2: False
Trace.WriteLine("n2 == n3: " + (n2 == n3)); // n2 == n3: True
// Numeric Operations
Trace.WriteLine("n1 + n2 = " + (n1 + n2).HasValue); // n1 + n2 = False
Trace.WriteLine("n1 * n2 = " + (n1 * n2).HasValue); // n1 * n2 = False
Trace.WriteLine("n1 / n2 = " + (n1 / n2).HasValue); // n1 / n2 = False
Trace.WriteLine("n2 + n3 = " + (n2 + n3).HasValue); // n2 + n3 = False
// A null in a numeric expression returns null. The coalescing operator is
// used to return a default value. The following shows different usages of the
// (??) operator.
int? nResult1 = (n1 + n2) ?? 0; // nResult1 = 0
int? nResult2 = (n1 ?? 0) + (n2 ?? 0); // nResult2 = 0
int? nResult3 = (n1 ?? 0) / (n2 ?? 1); // nResult3 = 0
}
/// <summary>
/// In the following code we serialize a nullable int and then deserialize it back.
/// Notice that the type of the generated XML element is int and not int?. The XML
/// element does not contain any indication that the integer may be missing. Yet,
/// as you can see from the node, it is missing.
///
/// When deserialing the value back, you get a NullReferenceException. This makes
/// sense when you think about it. You saved a nullable int, which could contain
/// every integer value as well as the null value. When you read that value back in,
/// you implicitly converted the int? to an int. The null value is not valid for a
/// value type, so your code generated an exception.
/// </summary>
public void TestNullableSerialization()
{
int? n = default(int?);
// Serialize a nullable filed
XmlSerializer serializer = new XmlSerializer(typeof(int?));
StringWriter writer = new StringWriter();
serializer.Serialize(writer, n);
// Show result of serializing a nullable int. The following is produced
// <?xml version="1.0" encoding="utf-16"?>
// <int xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:nil="true" />
Trace.WriteLine(writer.ToString());
// Now deserialize the XML stream
StringReader reader = new StringReader(writer.ToString());
int n2 = (int)serializer.Deserialize(reader); // Throws NullReferenceException exception
Trace.WriteLine("Deserialized int? : " + n2);
}
public void TestNullableBoxing()
{
int? n = default(int?); // n = null
string s = n.ToString(); // s = "";
int? n2 = default(int?); // n2 = null
object o = n2 as object; // Boxing n2. o = null
try
{
int? n3 = default(int?); // n3 = null
string s3 = (n3 as object).ToString(); // NullReferenceException
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
}
}
/// <summary>
/// Every nullable type can be implicitly converted to any interface that is implemented on the
/// contained non-nullable type. For example, you can use the IComparable<T> interface with any
/// numeric nullable type
/// </summary>
public void TestNullableBoxing2()
{
{
// A nullable type can be converted to any interface implemented by the contained
// non-nullable type
int? n1 = 1;
int? n2 = 2;
IComparable<int> nIC = n1 as IComparable<int>;
int nCompareResult = nIC.CompareTo(n2.Value);
}
// The following throws a NullReferenceException
{
int? n1 = default(int?);
int? n2 = 2;
IComparable<int> nIC = n1 as IComparable<int>; // nIC = null
int nCompareResult = nIC.CompareTo(n2.Value); // NullReferenceException is thrown
}
}
}
Using array parameters can expose your code to several unexpected
problems. It's much better to create method signatures that use alternative
representations to pass collections or variable-size arguments to methods.
The problem arises because arrays are covariant as input parameters. In other
words, you don't have to pass the exact type of the array into the method.
Furthermore, even though the array is passed by value, the contents of the array
can be references to reference types. Your method can change members of the
array in ways that will not work with some valid types. See
TestArrayCovariance1() and
TestArrayCovariance2() methods.
Furthermore, because arrays don't support contra variance, when you write array
members, your code will fail to compile even though it should. See
TestArrayContravariance1() method.
You can avoid those problems by typing parameters as interface types that create
a type-safe sequence to use. Input parameters should be typed as
IEnumerable<T> for some T.
This strategy ensures that you can't modify the input sequence, because
IEnumerable<T> does not provide any methods to
modify the collection. Another alternative is to pass types as base classes, a
practice that may also avoid APIs that support modifying the collection.
When you need to modify the sequence, it's best to use an input parameter of one
sequence and return the modified sequence. When you want to generate the
sequence, return the sequence as an IEnumerable<T>
for some T.
And yet there are times when you want to pass arbitrary options in methods.
That's when you can reach for an array of arguments. But make sure to use a
params array. The params
array allows the user of your method to simply place those elements as other
parameters. See TestParamsArray() method.
To summarize, arrays are not universally wrong method parameters, but they can
cause two types of errors. The array's covariance behavior causes runtime
errors. Whenever you use an array as a parameter to a method, there is almost
always a better alternative. If the parameter represents a sequence, use
IEnumerable<T> or a constructed
IEnumerable<T> for the proper type. If the parameter represents a mutable
collection, then rework the signature to mutate an input sequence and create the
output sequence. If the parameter represents a set of options, use a
params array. In all those cases, you'll end up with
a better, safer interface.
class Item47
{
/// <summary>
/// Shows that arrays are covariant; for example, a string[] is a subtype of object[],
/// therefore, you can pass a string[] when an object[] is expected.
/// </summary>
public void TestArrayCovariance1()
{
string[] numbers = { "one", "two", "three" };
TakesAnArray(numbers);
}
private void TakesAnArray(object[] aData)
{
try
{
// Assign an integer to aData. This would be possible if aData really were
// an array of objects, but since it really is an array of strings,
// we will get an ArrayTypeMismatchException with the following message:
// "Attempted to access an element as a type incompatible with the array".
aData[0] = 1;
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
}
}
public void TestArrayCovariance2()
{
// The following works
{
Base[] arr1 = new Base[5];
FillArray(arr1, () => Base.Factory());
Base[] arr2 = new Base[5];
FillArray(arr2, () => Derived1.Factory());
Base[] arr3 = new Base[5];
FillArray(arr3, () => Derived2.Factory());
}
// The following does not work. There is a mismatch between derived types
// ArrayTypeMismatchException: "Attempted to access an element as a type
//incompatible with the array".
{
Base[] arr1 = new Derived1[5];
FillArray(arr1, () => Base.Factory());
Base[] arr2 = new Derived1[5];
FillArray(arr2, () => Derived1.Factory());
Base[] arr3 = new Derived1[5];
FillArray(arr3, () => Derived2.Factory());
}
}
private void FillArray(Base[] array, Func<Base> generator)
{
for (int i = 0; i < array.Length; i++)
array[i] = generator();
}
public void TestArrayContravariance1()
{
Base[] array = new Base[5];
// Compiler error CS1503: "The best overloaded method match for 'PopulateArray(Derived1[])'
// has some invalid arguments"
//PopulateArray(array);
}
private void PopulateArray(Derived1[] array)
{
for (int i = 0; i < array.Length; i++)
array[i] = new Derived1();
}
public void TestParamsArray()
{
// Calling both DisplayArrayContentsViaArray and DisplayArrayContentsViaParamsArray
DisplayArrayContentsViaArray( new string[] {"one", "two", "three"} );
DisplayArrayContentsViaParamsArray("one", "two", "three");
// Note flexibility of using 'params' when no parameters need to be passed
//DisplayArrayContentsViaArray(); // CS1501: No overload for method 'DisplayArrayContentsViaArray' takes '0' arguments
DisplayArrayContentsViaArray(new string[] { }); // Note extra syntax!
DisplayArrayContentsViaParamsArray(); // OK
}
private void DisplayArrayContentsViaArray(object[] array)
{
foreach (object o in array)
Trace.WriteLine(o);
}
private void DisplayArrayContentsViaParamsArray(params object[] array)
{
foreach (object o in array)
Trace.WriteLine(o);
}
}
namespace Item47NS
{
class Base
{
public static Base Factory()
{
return new Base();
}
public virtual void DisplayType()
{
Trace.WriteLine("Base");
}
}
class Derived1 : Base
{
public static new Base Factory()
{
return new Derived1();
}
public override void DisplayType()
{
Trace.WriteLine("Derived1");
}
}
class Derived2 : Base
{
public static new Base Factory()
{
return new Derived2();
}
public override void DisplayType()
{
Console.WriteLine("Dervied2");
}
}
}
An object is not completely created until all constructors
have executed. In the meantime, virtual functions may not behave the way you'd
like or expect. Look at
TestVirtualInConstructor() method below. What will the output be?
Experienced C++ programmers would say, Base::foo.
Some C# programmers would say, Constructed in test method.
But the correct answer is, Set during initialization.
The base class constructor calls a virtual function that is defined in its class
but overridden in the derived class. At runtime, the derived class version gets
called. After all, the object's runtime type is Derived.
The C# language definition considers the derived object completely available,
because all the member variables have been initialized by the time any
constructor body is entered. In other words, the variable initializers have
executed, but this doesn't mean that you have necessarily initialized all your
member variables to the value you want via constructor parameters. Only the
variable initializers have executed; none of the code in any derived class
constructor body has had the chance to do its work.
Calling virtual functions in constructors works only under the strictest of
conditions. The derived class must initialize all instance variables properly in
variable initializers. That rules out quite a few objects: Most constructors
take some parameters that are used to set the internal state properly. So you
could say that calling a virtual function in a constructor mandates that all
derived classes define a default constructor, and no other constructor. But
that's a heavy burden to place on all derived classes. Do you really expect
everyone who ever uses your code to play by those rules?
class Item48
{
public void TestVirtualInConstructor()
{
Derived d = new Derived("Constructed in test method");
}
}
namespace Item48NS
{
class Base
{
protected Base()
{
foo();
}
protected virtual void foo()
{
Trace.WriteLine("Base::foo");
}
}
class Derived : Base
{
private readonly string msg = "Set during initialization";
public Derived(string msg)
{
this.msg = msg;
}
protected override void foo()
{
Trace.WriteLine(msg);
}
}
}
Sometimes you need large blocks of memory for certain algorithms.
And sometimes those blocks of memory are needed only occasionally. It seems that
you are stuck between two bad alternatives: You could create a local variable,
thereby generating a huge heap of garbage every time you run that algorithm. Or
you could create a member variable and lock up a large amount of memory for long
periods. There are times when neither of those options feels correct.
There is a third option: Create a weak reference (represented in .NET by
WeakReference class). Weak references are
almost garbage. You tell the garbage collector that an object is ready
to be collected, but you keep a handle to it just in case you want it back
before it's collected. When you do it correctly, using a weak reference lets you
work with the garbage collector, instead of fighting it, to optimize memory
usage. Weak references are useful for objects that use a lot of memory,
but can be recreated easily if they are reclaimed by garbage collection.
Using a weak reference is simple. You create a new weak reference, telling it to
hold on to an object that you are finished using, and then you set the strong
reference to null:
WeakReference w = new WeakReference (myLargeObject);
myLargeObject = null;
Now the object referred to by myLargeObject
is considered garbage by the system. If the garbage collector runs, it will
collect it. However, suppose you need a myLargeObject
again before the garbage collector runs. You simply ask
WeakReference for the object:
myLargeObject = w.Target as MyLargeClass;
if (myLargeObject == null)
myLargeObject = new MyLargeClass();
The Target property returns null if the object has already been garbage-collected. Then you have no choice except to create a new object. You've off-loaded a very hard optimization problem to the runtime. You get to reuse objects when they are still around, but you let the system reclaim the memory resources when necessary. If the weak reference is reclaimed as a strong reference, it has the same contents and state that it had before. If creating the state is what's expensive, then you should keep a strong reference. A WeakReference is for the case when allocating the memory is expensive.
Also note that objects that implement
IDisposable interface are not good candidates for weak references.
You have no way of knowing when to call Dispose() on
those objects. You cannot know
when you should call Dispose() on an object that you
turn into a weak reference.
In practice, weak references help you when you have objects that are very large
and are needed only intermittently by an algorithm in your application. Weak
references work best for objects that do not need to support
IDisposable. Almost by definition, these classes do
not have finalizers. Within those constraints, weak references work well to let
the garbage collector efficiently manage your memory needs.
This advice must come with a considerable caution and warning: Weak references
may make your algorithm work faster, but they also have a large impact on the
performance of the garbage collector. Because of that impact, adding weak
references to your program may make it much slower. Before you consider weak
references, optimize your algorithms. Try to rework them to create less memory
pressure overall (see Item 37
Prefer Lazy Evaluation Queries). Only after those approaches have been
exhausted should you consider using weak references. Then benchmark your
application with and without weak references, and carefully measure the
difference.
class Item49
{
/// <summary>
/// Could not get the following example to work
/// </summary>
public void TestWeakReference()
{
// Create a large object
MyClass ob = new MyClass();
ob.MakeSomeGarbage();
// Create a weak reference
WeakReference wr = new WeakReference(ob, false);
// After creating a weak reference, remove the 'strong reference' that this application has
// to object 'ob'
ob = null;
// Garbage collection
GC.Collect();
// Check weak reference to our large object
while (wr.IsAlive)
{
//obLarge = (MyClass)wr.Target;
//strStatus = (obLarge == null) ? "Object is null" : "Object is alive";
Trace.WriteLine("Object is alive");
System.Threading.Thread.Sleep(1000);
}
MyClass obLarge = (MyClass)wr.Target;
string strStatus = (obLarge == null) ? "Object is null" : "Object is alive";
Trace.WriteLine(strStatus);
}
}
namespace Item49NS
{
public class MyClass
{
private int[,] matrix = new int[1000, 1000];
~MyClass()
{
Trace.WriteLine("MyClass Finalized.");
}
public void MakeSomeGarbage()
{
// Create objects and release them to fill up memory with unused objects.
for (int i = 0; i < matrix.GetUpperBound(0); i++)
for (int j = 0; j < matrix.GetUpperBound(1); j++)
matrix[i, j] = i * j;
}
}
}
You can increase the readability of your code by using implicit properties:
public string Name {get; set; }
The compiler creates the backing field using a compiler-generated name. You can
even use the property setter to modify the value of the backing field. Because
the name of the backing field is compiler generated, even inside your own class
you need to call the property accessor rather than modify the backing field
directly.
Implicit properties support the same property access specifiers as do their
explicit counterparts. You can define any more-restrictive set accessor you
need. See class ImplicitProperties1 below.
Of course, because implicit properties generate the same code as explicit
properties, you can use implicit properties to define virtual properties,
override virtual. properties, or implement a property defined in an
interface. When you create a virtual implicit property, derived classes do not
have access to the compiler-generated backing store. However, overrides can
access the base property get and set methods just as they can with any other
virtual method. See class ImplicitProperties2
You gain two additional advantages by using implicit properties. When the time
comes to replace the implicit property with a concrete implementation because of
data validation or other actions, you are making binary-compatible changes to
your class, and your validation will be in only one location. See classes
Person and PersonImproved
below.
However, implicit properties have some limitations when it comes to creating
immutable types. Even inside your constructor, you must use the property
accessor to set the values of your properties. The backing field must support
changes, no matter when you call the set accessor. It can't tell the difference
between setting the value during construction and setting the value from some
other method. This means that your type does not have the
initonly flag on the backing field. Furthermore, you could write any
mutator methods and have mutator methods sneak into your type. Thus, when you
create immutable types, you should use explicit member variables.
The only way to create a true immutable type is to write the properties and
backing fields yourself using a concrete implementation. Implicit properties do
not support true immutable types at the JIT level.
There is one other important limitation of implicit properties. You cannot use
implicit properties on types that are decorated with the Serializable attribute.
The persistent file storage format depends on the name of the compiler-generated
field used for the backing store. That field name is not guaranteed to remain
constant. It may change at any time when you modify the class.
namespace Item50NS
{
// Implicit properties support the same property access specifiers as do their explicit
public class ImplicitProperties1
{
public string Name1
{
get;
protected set;
}
public string Name2
{
get;
internal set;
}
public string Name3
{
private get;
set;
}
// etc.
//Error: "Cannot specify accessibility modifiers for both accessors of the property
// or indexer 'MoreEffectiveCS.Misc.Item50NS.ImplicitProperties.Name4'"
/*public string Name4
{
protected internal get;
protected internal set;
}*/
}
// You can use implicit properties to define virtual properties, override virtual
// properties, or implement a property defined in an interface.
public class BaseImplicitProperties
{
public virtual string Name
{
get;
protected set;
}
}
public class ImplicitProperties2 : BaseImplicitProperties
{
public override string Name
{
get {return base.Name;}
protected set
{
if (!string.IsNullOrEmpty( value))
base.Name = value;
}
}
}
// Implicit properties can be easily replaced with concrete implementations
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class PersonImproved
{
private string _strFirstName;
public string FirstName
{
get { return _strFirstName; }
set
{
if (string.IsNullOrEmpty(value))
throw new ArgumentException("First name cannot be null or empty");
else
_strFirstName = value;
}
}
}
}