I'm a beginner in C#, just a question on Func delegate:
public delegate TResult Func<in T,out TResult>(T arg);
I can understand the need to place in
keyword before T as we don't want to modify the source input, but what about out
before TResult? isn't that out means we need to modify the ouput but why? don't we sometimes just generate the return object on the fly, lets say we have a delegate:
Func<string, bool> nameFilter = str => str[0] == 'S';
so it checks a string to see if its first character is 'S' then return true or false, so we dynamically return this boolean value, what does out keyword do here? there is nothing nneded to be changed to return?
You rarely need to worry about the in
and out
keywords in Generic type definitions. A class defined with in
/ out
generic type parameters will usually "just work" when you're consuming it, and I'd wager that most devs will never write such a definition in their own code.
To get a complete explanation, you should read Covariance and Contravariance and Variance in Delegates . The rest of my answer is just some illustrative example code.
To simplify the explanation, I'm going explain in
and out
separately through Action<T>
and Func<TResult>
instead of Func<T,TResult>
.
The examples all use the following two classes:
class BaseClass {}
class DerivedClass : BaseClass {}
out
For this example, I've mimicked Func<out TResult>
, but removed the out
(covariance) modifier to demonstrate its effect. Covariance allows us to use a func that returns DerivedType
anywhere that expects a func that returns BaseType
.
class CovarianceExamples
{
// This is similar to System.Func<out TResult>(), but with covariance removed
delegate TResult InvariantFunc<TResult>();
void InvariantFuncExample()
{
// Ignore the values of these variables; it's the types that are important
InvariantFunc<BaseClass> baseFunc = null;
InvariantFunc<DerivedClass> derivedFunc = null;
baseFunc = baseFunc; // Allowed
baseFunc = derivedFunc; // Not allowed; compile error!
}
void CovariantFuncExample()
{
// Ignore the values of these variables; it's the types that are important
Func<BaseClass> baseFunc = null;
Func<DerivedClass> derivedFunc = null;
baseFunc = baseFunc; // Allowed
baseFunc = derivedFunc; // Allowed
}
}
in
For this example, I've mimicked Action<in T>
, but removed the in
(contravariance) modifier to demonstrate its effect. Contravariance allows us to use an action that accepts BaseType
anywhere that expects an action that accepts DerivedType
.
class ContravarianceExamples
{
// This is similar to System.Action<in T>(T), but with contravariance removed
delegate void InvariantAction<T>();
void InvariantActionExample()
{
// Ignore the values of these variables; it's the types that are important
InvariantAction<BaseClass> baseAction = null;
InvariantAction<DerivedClass> derivedAction = null;
baseAction = baseAction; // Allowed
derivedAction = baseAction; // Not allowed; compile error!
}
void ContravariantActionExample()
{
// Ignore the values of these variables; it's the types that are important
Action<BaseClass> baseAction = null;
Action<DerivedClass> derivedAction = null;
baseAction = baseAction; // Allowed
derivedAction = baseAction; // Allowed
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.