R
raylopez99
Here is a good example that shows generic delegate types. Read this
through and you'll have an excellent understanding of how to use these
types. You might say that the combination of the generic delegate
type expression in just the right place and a well-named method means
we can almost read the code out loud and understand it without even
thinking.
Note that since 'boxing' and 'unboxing' is involved (I think), you
don't get what you expect if you pass a value type (see the output
labeled "//not what you want - baad!")
To get around this problem, you have to create a delegate parameter
that accepts an object (reference type). Then it works.
However, just to show how 'ref' works, when the generic method called
passes by reference rather than by object (required whenever 'new' is
used in a method) the delegate had to be renamed (from Transformer to
'Uransformer'). Don't know why this is the case but that's the way it
is.
Finally, note how you can change an object whether your pass by value
or by reference, if you are passing the object. This holds even for
strings. Note the strings are changed in both versions of the generic
delegates, "Transform2" and "Transform3".
What to make of 'generic delegate types'? After going through this
example carefully (which was a modification of a extremely primitive
example of generic delegate types taken from p. 108 of the C# 3.0
Nutshell, which formed the first output, involving squaring a value
type), I am convinced generic delegate types are a receipe for
disaster, since essentially they do away with strong typing (whatever
that means, but I have an understanding in my mind's eye).
The only advantage I can see is just a slick way of declaring a
delegate " public delegate T Transformer<T> (T arg); " that will then
allow you to write methods/functions that obey this template of taking
a type T and returning a type T (whether the T is an int, a double, or
even an object), though the methods/functions are radically
different. These methods can then be called using the convention
below. Big deal. A receipe for disaster.
Big deal and a receipe for disaster is not unlike "lambda expressions"
and "anonymous delegates" (both topics I'm not that familiar with).
If anybody can relate how generic delegate types correlate with these
two topics it would be appreciated.
RL
(c) 2008, all rights reserved, by 'artistic license' and void where
prohibited. For skolarly use only. The moral rights of the author are
preserved, whatever that means. Copyleft and free to use without
attribution.
// output
1X! 2X! 3X! 1Sq! 4Sq! 9Sq! 1Cb! 64Cb! 729Cb! //note this works as
expected, since 9x9x9 = 729, and each array element is being changed
as expected.
value now [X]: 2 //not what you want - baad!
value now [Square]: 2 //not what you want - baad!
value now [Cube]: 2 //not what you want - baad!
____________
value now [X]: 3 //not what you want - baad!
value now [Square]: 3 //not what you want - baad!
value now [Cube]: 3 //not what you want - baad!
_____UtilSquarer______
value now [UtilSquarer]: 9 //now works fine
value of string [UtilSquarer]: goodbye!
_____UtilStringChanger______
value of string [UtilStringChanger]: BAA_is_a_goodBuy! //string
changed properly (1 of 2 ways to change string; see below for ref
version)
__UtilStringChanger2AndMoreStaticVer__
values of string, int are [UtilStringChanger2AndMoreStaticVersion]:
anotherStrin
gHere! , 10001
Press any key to continue . . .
/////////////////////
using System;
using System.Collections.Generic;
using System.Text;
namespace p108DelegatesCSNutshell
{
public delegate T Transformer<T> (T arg);
public delegate T Uransformer<T> (ref T arg); //slight name change
needed for 'ref' version, otherwise won't compile
class Program
{
static void Main(string[] args)
{
int[] values = new int[] { 1, 2, 3 };
int value = 2;
Util myUtil = new Util();
Util.Transform1(values, ReturnX); //dynamically hook
ReturnX
foreach (int i in values)
{
Console.Write(i + "X! ");
}
Util.Transform1(values, Square); //dynamically hook square
foreach (int i in values)
{
Console.Write(i + "Sq! ");
}
Util.Transform1(values, myUtil.Cube); //dynamically hook
cube
foreach (int i in values)
{
Console.Write(i + "Cb! ");
}
Console.WriteLine("\n");
myUtil.Transform2(value, ReturnX);
Console.WriteLine("value now [X]: {0}", value);
myUtil.Transform2(value, Square);
Console.WriteLine("value now [Square]: {0}", value);
myUtil.Transform2(value, myUtil.Cube);
Console.WriteLine("value now [Cube]: {0}", value);
Console.WriteLine("____________ \n");
myUtil.Transform2(myUtil.j, ReturnX);
Console.WriteLine("value now [X]: {0}", myUtil.j);
myUtil.Transform2(myUtil.j, Square);
Console.WriteLine("value now [Square]: {0}", myUtil.j);
myUtil.Transform2(value, myUtil.Cube);
Console.WriteLine("value now [Cube]: {0}", myUtil.j);
Console.WriteLine("_____UtilSquarer______ \n");
myUtil.Transform2(myUtil, myUtil.UtilSquarer);
Console.WriteLine("value now [UtilSquarer]: {0}",
myUtil.j);
Console.WriteLine("value of string [UtilSquarer]: {0}",
myUtil.s);
Console.WriteLine("_____UtilStringChanger______ \n");
myUtil.Transform3(ref myUtil, myUtil.UtilStringChanger);
Console.WriteLine("value of string [UtilStringChanger]:
{0}", myUtil.s);
Console.WriteLine("__UtilStringChanger2AndMoreStaticVer__
\n");
myUtil.Transform3(ref myUtil,
UtilStringChanger2AndMoreStaticVersion);
Console.WriteLine("values of string, int are
[UtilStringChanger2AndMoreStaticVersion]: {0} , {1}", myUtil.s,
myUtil.j);
}
static int ReturnX(int x)
{
return x;
}
static int Square(int x)
{
return x * x;
}
static Util UtilStringChanger2AndMoreStaticVersion(ref Util U)
{
U = new Util();
U.s = "anotherStringHere!";
U.j = 10001;
return U;
}
}
}
/////////////////////
using System;
using System.Collections.Generic;
using System.Text;
namespace p108DelegatesCSNutshell
{
class Util
{
public int j;
public string s;
public Util ()
{
s = "hi";
j = 3;
}
public static void Transform1<T>(T[] values, Transformer<T> t)
{
for (int i = 0; i < values.Length; i++)
values = t(values);
}
public int Cube(int x) { return x * x * x; }
public Util UtilSquarer(Util U)
{
U.j = U.j * U.j;
U.s = "goodbye!";
//U = new Util(); //next 3 lines won't work here since ref
not passed
//U.j = U.j * U.j;
//U.s = "NoGo";
return U;
}
public Util UtilStringChanger(ref Util U)
{
U = new Util();
U.s = "BAA_is_a_goodBuy!";
return U;
}
public void Transform2<T>(T value, Transformer<T> t)
{ value = t(value); }
public void Transform3<T>(ref T value, Uransformer<T> t)
{
value = t(ref value);
}
}
}
through and you'll have an excellent understanding of how to use these
types. You might say that the combination of the generic delegate
type expression in just the right place and a well-named method means
we can almost read the code out loud and understand it without even
thinking.
Note that since 'boxing' and 'unboxing' is involved (I think), you
don't get what you expect if you pass a value type (see the output
labeled "//not what you want - baad!")
To get around this problem, you have to create a delegate parameter
that accepts an object (reference type). Then it works.
However, just to show how 'ref' works, when the generic method called
passes by reference rather than by object (required whenever 'new' is
used in a method) the delegate had to be renamed (from Transformer to
'Uransformer'). Don't know why this is the case but that's the way it
is.
Finally, note how you can change an object whether your pass by value
or by reference, if you are passing the object. This holds even for
strings. Note the strings are changed in both versions of the generic
delegates, "Transform2" and "Transform3".
What to make of 'generic delegate types'? After going through this
example carefully (which was a modification of a extremely primitive
example of generic delegate types taken from p. 108 of the C# 3.0
Nutshell, which formed the first output, involving squaring a value
type), I am convinced generic delegate types are a receipe for
disaster, since essentially they do away with strong typing (whatever
that means, but I have an understanding in my mind's eye).
The only advantage I can see is just a slick way of declaring a
delegate " public delegate T Transformer<T> (T arg); " that will then
allow you to write methods/functions that obey this template of taking
a type T and returning a type T (whether the T is an int, a double, or
even an object), though the methods/functions are radically
different. These methods can then be called using the convention
below. Big deal. A receipe for disaster.
Big deal and a receipe for disaster is not unlike "lambda expressions"
and "anonymous delegates" (both topics I'm not that familiar with).
If anybody can relate how generic delegate types correlate with these
two topics it would be appreciated.
RL
(c) 2008, all rights reserved, by 'artistic license' and void where
prohibited. For skolarly use only. The moral rights of the author are
preserved, whatever that means. Copyleft and free to use without
attribution.
// output
1X! 2X! 3X! 1Sq! 4Sq! 9Sq! 1Cb! 64Cb! 729Cb! //note this works as
expected, since 9x9x9 = 729, and each array element is being changed
as expected.
value now [X]: 2 //not what you want - baad!
value now [Square]: 2 //not what you want - baad!
value now [Cube]: 2 //not what you want - baad!
____________
value now [X]: 3 //not what you want - baad!
value now [Square]: 3 //not what you want - baad!
value now [Cube]: 3 //not what you want - baad!
_____UtilSquarer______
value now [UtilSquarer]: 9 //now works fine
value of string [UtilSquarer]: goodbye!
_____UtilStringChanger______
value of string [UtilStringChanger]: BAA_is_a_goodBuy! //string
changed properly (1 of 2 ways to change string; see below for ref
version)
__UtilStringChanger2AndMoreStaticVer__
values of string, int are [UtilStringChanger2AndMoreStaticVersion]:
anotherStrin
gHere! , 10001
Press any key to continue . . .
/////////////////////
using System;
using System.Collections.Generic;
using System.Text;
namespace p108DelegatesCSNutshell
{
public delegate T Transformer<T> (T arg);
public delegate T Uransformer<T> (ref T arg); //slight name change
needed for 'ref' version, otherwise won't compile
class Program
{
static void Main(string[] args)
{
int[] values = new int[] { 1, 2, 3 };
int value = 2;
Util myUtil = new Util();
Util.Transform1(values, ReturnX); //dynamically hook
ReturnX
foreach (int i in values)
{
Console.Write(i + "X! ");
}
Util.Transform1(values, Square); //dynamically hook square
foreach (int i in values)
{
Console.Write(i + "Sq! ");
}
Util.Transform1(values, myUtil.Cube); //dynamically hook
cube
foreach (int i in values)
{
Console.Write(i + "Cb! ");
}
Console.WriteLine("\n");
myUtil.Transform2(value, ReturnX);
Console.WriteLine("value now [X]: {0}", value);
myUtil.Transform2(value, Square);
Console.WriteLine("value now [Square]: {0}", value);
myUtil.Transform2(value, myUtil.Cube);
Console.WriteLine("value now [Cube]: {0}", value);
Console.WriteLine("____________ \n");
myUtil.Transform2(myUtil.j, ReturnX);
Console.WriteLine("value now [X]: {0}", myUtil.j);
myUtil.Transform2(myUtil.j, Square);
Console.WriteLine("value now [Square]: {0}", myUtil.j);
myUtil.Transform2(value, myUtil.Cube);
Console.WriteLine("value now [Cube]: {0}", myUtil.j);
Console.WriteLine("_____UtilSquarer______ \n");
myUtil.Transform2(myUtil, myUtil.UtilSquarer);
Console.WriteLine("value now [UtilSquarer]: {0}",
myUtil.j);
Console.WriteLine("value of string [UtilSquarer]: {0}",
myUtil.s);
Console.WriteLine("_____UtilStringChanger______ \n");
myUtil.Transform3(ref myUtil, myUtil.UtilStringChanger);
Console.WriteLine("value of string [UtilStringChanger]:
{0}", myUtil.s);
Console.WriteLine("__UtilStringChanger2AndMoreStaticVer__
\n");
myUtil.Transform3(ref myUtil,
UtilStringChanger2AndMoreStaticVersion);
Console.WriteLine("values of string, int are
[UtilStringChanger2AndMoreStaticVersion]: {0} , {1}", myUtil.s,
myUtil.j);
}
static int ReturnX(int x)
{
return x;
}
static int Square(int x)
{
return x * x;
}
static Util UtilStringChanger2AndMoreStaticVersion(ref Util U)
{
U = new Util();
U.s = "anotherStringHere!";
U.j = 10001;
return U;
}
}
}
/////////////////////
using System;
using System.Collections.Generic;
using System.Text;
namespace p108DelegatesCSNutshell
{
class Util
{
public int j;
public string s;
public Util ()
{
s = "hi";
j = 3;
}
public static void Transform1<T>(T[] values, Transformer<T> t)
{
for (int i = 0; i < values.Length; i++)
values = t(values);
}
public int Cube(int x) { return x * x * x; }
public Util UtilSquarer(Util U)
{
U.j = U.j * U.j;
U.s = "goodbye!";
//U = new Util(); //next 3 lines won't work here since ref
not passed
//U.j = U.j * U.j;
//U.s = "NoGo";
return U;
}
public Util UtilStringChanger(ref Util U)
{
U = new Util();
U.s = "BAA_is_a_goodBuy!";
return U;
}
public void Transform2<T>(T value, Transformer<T> t)
{ value = t(value); }
public void Transform3<T>(ref T value, Uransformer<T> t)
{
value = t(ref value);
}
}
}