简体   繁体   中英

out parameter modifier in Func delegate (C#)

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?

The Short Answer

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.

The Longer Answer

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 {}

Covariance: 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
    }
}

Contravariance: 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM