Information Technology Reference
In-Depth Information
contravariant (
in
), and method return types are covariant (
out
). The BCL
updated many of their delegate definitions to include variance:
public delegate
TResult
Func
<
out
TResult>();
public delegate
TResult
Func
<
in
T,
out
TResult>(T arg);
public delegate
TResult
Func
<
in
T1, T2,
out
TResult>(T1 arg1,
T2 arg2);
public delegate void
Action
<
in
T>(T arg);
public delegate void
Action
<
in
T1,
in
T2>(T1 arg1, T2 arg2);
public delegate void
Action
<
in
T1,
in
T2, T3>(T1 arg1,
T2 arg2, T3 arg3);
Again, this probably isn't too hard. But, when you mix them, things start
to get to be mind benders. You already saw that you cannot return invari-
ant interfaces from covariant interfaces. You can't use delegates to get
around the covariant and contravariant restrictions, either.
Delegates have a tendency to “flip” the covariance and contravariance in
an interface if you're not careful. Here are a couple examples:
public interface
ICovariantDelegates
<out T>
{
T GetAnItem();
Func
<T> GetAnItemLater();
void
GiveAnItemLater(
Action
<T> whatToDo);
}
public interface
IContravariantDelegates
<
in
T>
{
void
ActOnAnItem(T item);
void
GetAnItemLater(
Func
<T> item);
Action
<T> ActOnAnItemLater();
}
I've named the methods in these interfaces specifically to show why it
works the way it does. Look closely at the ICovariantDelegate interface
definition. GetAnItemLater() is just a way to retrieve an item lazily. The
caller can invoke the Func<T> returned by the method later to retrieve a
value. T still exists in the output position. That probably still makes sense.
The GetAnItemLater() method probably is a bit more confusing. Here,
you're method takes an delegate that will accept a T object whenever you
call it. So, even though Action<in T> is covariant, its position in the