This is kind of a re-blog. My goal is to have a more specific blog than before.
Generic Delegates
1: public static int Method1()
2: {
3: Trace.WriteLine("Invoked Method1");
4: return 1;
5: }
6: public static int Method2()
7: {
8: Trace.WriteLine("Invoked Method2");
9: return 1;
10: }
11: public static int Method3()
12: {
13: Trace.WriteLine("Invoked Method3");
14: return 1;
15: }
16: // Code to demonstrate delegate usage
17: private void cmdGenericDelegates_Click(object sender, EventArgs e)
18: {
19: Func<int> brunoDelegate1 = Method1;
20: Func<int> brunoDelegate2 = Method2;
21: Func<int> brunoDelegate3 = Method3;
22:
23: Func<int> totalDelegates =
24: brunoDelegate1 +
25: brunoDelegate2 +
26: brunoDelegate3;
27:
28: Delegate[] delegateList = totalDelegates.GetInvocationList();
29: foreach (Func<int> curr_instance in delegateList)
30: {
31: curr_instance(); // Basically, this runs Method1(), Method2(), Method3()
32: }
33: }
The point here is the curr_instance() will call all 3 methods:
delegateList is essentially an array of refererences to methods:
Sample Code: Building a data model to experiment with
1: public class Person
3: public Person(string fName, string lName)
4: {
5: this.firstName = fName;
6: this.lastName = lName;
7: }
8: public virtual void Show()
9: {
10: Trace.WriteLine("Name is " + firstName + " " + lastName);
11: }
12: public string firstName;
13: public string lastName;
14: }
15: public class Employee : Person
16: {
17: public Employee(string title, string fName, string lName)
18: : base(fName, lName)
19: {
20: this.title = title;
21: }
22: public override void Show()
23: {
24: base.Show();
25: Trace.WriteLine("Title is " + title);
26: }
27: public string title;
28: }
29: public class Manager : Person
31: public Manager(int numberEmployees, string fName, string lName)
32: : base(fName, lName)
33: {
34: this.numberEmployees = numberEmployees;
35: }
36: public int numberEmployees;
37: }
38: //
39: //Make the class People enumerable on Person
40: //
41: public class People : IEnumerable<Person>
42: {
43: private Person[] people;
44: public People(Person[] pArray)
45: {
46: people = new Person[pArray.Length];
47: for (int i = 0; i < pArray.Length; i++)
48: {
49: people[i] = pArray[i];
50: }
51: }
52: //This enumerator returns a list of person objects,
53: //one at a time.
54: public IEnumerator<Person> GetEnumerator()
55: {
56: foreach (Person p in people)
57: {
58: yield return p;
59: }
60: }
61: // Needed for to make compiler happy, but not used in my code.
62: IEnumerator IEnumerable.GetEnumerator()
63: {
64: foreach (Person p in people)
65: {
66: yield return p;
67: }
68: }
69: }
Generic Enumerators
Challenges: How type safety is enforced in VS 2010
What is legal and not legal when it comes to subclassing with generics
The scenario we are trying understand well is that we want generics to wok with the same we get concrete types to work.
Let's start with the concrete types. Suppose you have 2 classes as follows:
This has always been legal in C# 3.0
But this is not legal
However when dealing with generics, in spite of what your intuition would tell you, this is not legal:
This is needed because we don't know if we are going to add a "SubContractor," which might also be derived class of a person.
Imagine a scenario where you have an array like this:
Imagine loop through this array and trying to access the "Salary." Everything works fine until you get to the "SubContractor," where "Hours" and "HourlyRate" are the data elements. We'd get a crash.
That is what the type system is protecting us from.
I disagree completely with your conclusion, and here's why: The example you give is looping over a collection of Person looking for a Salary property. If the Salary property is not part of the base class Person and only part of the subclass Employee, then the compiler error should be that the base class Person does not have a Salary field.
The last compiler error in your post is, in fact, a lie from the compiler, and the only reason a compiler would lie is if it has a bug. Since converting an Employee to a Person is always legal, then we are guaranteed that converting a List<Employee> to an IList<Person> is always safe, and any claim by the compiler that it might not be safe is factually incorrect. Obviously, since our variables are now declared as type Person and not as type Employee, we only have access to those members which are included in the base class, which is exactly what happens when we perform the same cast on a single item. That, therefore, is the final proof of a compiler bug, handling an identical situation differently just because we happen to be inside a generic collection.
Hey, this blog template is badly messed up in firefox.
Will respond to both issues very soon.