locked
Action<T> : Specifying Type-Argument upon Invoke RRS feed

  • Question

  • I want to specify the Type-Argument of my instance of Action<T> when i call it, so that i could use it with different types, is there a way to do this?

    *** I DONT WANT TO CALL A METHOD INSTEAD OF ACTION<T>

    Action<T> action1 = parameter1 => { }; action1<int>(123);

    Tuesday, October 23, 2012 6:54 AM

Answers

  • When defining a delegate, just as when you're defining a method, you can supply a generic argument:

    public class MyClass //note not generic
    {
        public delegate void MyAction<T>(T param);
    }

    When you create an actual instance of that delegate you need to provide an actual type for the generic arguments, just like when calling a generic method you need to provide an actual type.

    In any case, it seems you don't have any actual use case here; there is no compelling reason you have to *want* to do this.  Rather than asking yourself, "why doesn't the language let me do this?" you must first ask yourself, "why should the language let me do this?".  Language features are, by default, unimplemented.  They didn't go through every single possible feature and choose to not implement most of them.

    Now, given that, what you're wanting to do really isn't possible anyway.  If you want to define a variable of type "Action" but without specifying the value of the generic constraint then what could you possibly assign to that variable?  The entire point of a delegate is to hold onto a reference to a function (and possibly an instance of the type the function is defined in) with a Known signature.  That "known signature" is *very* important.  An "Action<int>" is a reference to a function that takes an int and returns void, an "Action<string>" is a reference to a function that takes a string and returns void.  What is an "Action<T>"  It's a function that takes an unknown parameter and returns nothing.  Well, there are no functions that take an unknown parameter, all functions in C# must take *something* as a parameter, so you couldn't ever assign anything to that action.

    Friday, October 26, 2012 3:45 PM
  • "but my question is why C#-Compiler should not understand this when it can simply understand the other?!"

    And my question is "why should it support this?"

    I'm pretty sure that one could design a language similar to C# and that has such a feature. But in doing so one needs to decide how to implement such a thing.

    One solution would be to treat variables like action1 in a special way: it wouldn't be an actual variable, one that exists at runtime. It would be just some kind of symbol that is associated with a generic anonymous method and would allow you to call that method. Something similar to "using foo = somenamespace.bar".

    At this point it becomes a confusing feature, it looks like a variable but it's not a variable. It has a delegate type but it doesn't create any delegate instances and in fact doesn't exist at runtime at all.

    Perhaps a syntax that at least doesn't involve delegates would be more appropriate:

    let action1<T> = x => {};

    action1<int>();

    Even so you still have to answer the main question, what for?

    Friday, October 26, 2012 3:57 PM

All replies

  • Just like this:

    Action<int> myAction = (number)=>{//Do what u want……;};
    myAction(2);

    我的博客园
    慈善点击,点击此处
    和谐拯救危机,全集下载,净化人心

    Tuesday, October 23, 2012 7:11 AM
  • If you want an action that has one parameter and can take any type as that one parameter all you need is an "Action<object>".  You can then pass anything you want as the parameter to that action.
    • Proposed as answer by JMCF125 Tuesday, October 23, 2012 2:49 PM
    • Unproposed as answer by Kiarash.Nakhaei Wednesday, October 24, 2012 6:29 AM
    Tuesday, October 23, 2012 2:17 PM
  • I know i can declare my Action delegate with 'Object' as the Type-Argument, but the thing is I WANT TO DECLARE MY DELEGATE INSTANCE TYPE-FREE, that is with that T in there, and later specify it in the Call-Sight, exactly as i demonstrated, not to dodge the problem by declaring the 'Action' with 'Object' Type-Argument.

    So far C#'s syntax capabilities have shown that this is possible to express - the C# syntax has the required complexity to express this requirement - but it would generete errors unless put in a method which has a Type-Argument:

    public void Method1<T>()
    {
        Action<T> action1 = x => {};
    }

    What i want to say is that, this should be possible to do, perhaps C#-Compiler should understand this because the langueage (i.e. C#) has the required complexity to express this need.

    Wednesday, October 24, 2012 6:28 AM
  • Yes you can. You can call generic methods with different type arguments whenever and where ever you want.

    The code below uses reflection:

    using System;
    using System.Reflection;
    
    namespace ConsoleApplication4 {
    
        class Program {
    
            static void Main(string[] args) {
    
                // Call the GENERIC method with either a string or a number
                GenericMethod<string>("100 STR");
                GenericMethod<int>(100);
    
                // Call the GENERIC method with either a string or a number (TYPE IS INFERRED)
                GenericMethod("200 STR");
                GenericMethod(200);
    
                // Call the NON-GENERIC method with either a string or a number
                NonGenericMethodWithTypeSpecified(typeof(string), "300 STR");
                NonGenericMethodWithTypeSpecified(typeof(int), 300);
    
                // Call the NON-GENERIC method with either a string or a number (TYPE IS INFERRED)
                NonGenericMethodWithNOTypeSpecified("400 STR");
                NonGenericMethodWithNOTypeSpecified(400);
    
                Console.ReadLine();
            }
    
            /// <summary>
            /// The NON generic method is simply a wrapper for the generic 
            /// method using reflection.
            /// </summary>
            static void NonGenericMethodWithTypeSpecified(Type typeArg, object value) {
    
                const BindingFlags BindingFlags = BindingFlags.Static | BindingFlags.NonPublic;
    
                typeof(Program)
                    .GetMethod("GenericMethod", BindingFlags)
                    .MakeGenericMethod(typeArg)
                    .Invoke(
                        /* object = */ null /* static class, no "this" */, 
                        /* parameters = */ new object[] { value }
                        );
            }
    
            /// <summary>
            /// The NON generic method is simply a wrapper for the generic 
            /// method using reflection.
            /// </summary>
            static void NonGenericMethodWithNOTypeSpecified(object value) {
    
                const BindingFlags BindingFlags = BindingFlags.Static | BindingFlags.NonPublic;
    
                typeof(Program)
                    .GetMethod("GenericMethod", BindingFlags)
                    .MakeGenericMethod(value != null ? value.GetType() : typeof(object))
                    .Invoke(
                    /* object = */ null /* static class, no "this" */,
                    /* parameters = */ new object[] { value }
                        );
            }
    
            /// <summary>
            /// A generic method. Simply writes the parameter to the console.
            /// </summary>
            static void GenericMethod<T>(T param) {
    
                /* ... do whatever ... */
    
                Console.WriteLine("You passed: {0}", param != null ? param.ToString() : "<NULL>");
            }
        }
    }


    If reflection is  slow, you may want to cache some of the results, like the MethodInfo, and then call the MakeGenericType() as needed.

    Console Output Results
    Wednesday, October 24, 2012 7:09 AM
  • @ I WANT TODECLARE MY DELEGATE INSTANCE TYPE-FREE

    Please first use lower letters with upper ones, yours isn't polite.

    Then come back to your question——"T" is a generic type, why do you keep it "Free"?I don't understand what you want——Do u wanna that you can pass anything to be instead of the "T"?


    我的博客园
    慈善点击,点击此处
    和谐拯救危机,全集下载,净化人心

    Wednesday, October 24, 2012 7:11 AM
  • Dear Martin Lottering,

    I can call methods with what ever Type-Arguments i may, with or without specifying the Type-Argument itself; that is what generics are for. The question is about delegates not methods - and not in an explicit way that is to provide the type on the Call-Sight.

    Wednesday, October 24, 2012 10:41 AM
  • "The question is about delegates not methods - and not in an explicit way that is to provide the type on the Call-Sight."

    There's no way to do exactly what you want.

    First and foremost a delegate instance has a type and that type can't ever be a open type, it will always be something like Action<int>, Action<string> etc.

    And second, when you create a delegate instance you have to specify a mehod and the method type must match the delegate type. To specify the delegate type argument on the callsite the delegate would need to somehow bind to multiple methods and when you try to invoke it with a type argument it would need to perform overload resolution. That's not possible.

    "I can call methods with what ever Type-Arguments i may, with or without specifying the Type-Argument itself; that is what generics are for."

    Generics are in general a compile time feature. What you are asking is a runtime feature.

    Wednesday, October 24, 2012 11:07 AM
  • I know i can declare my Action delegate with 'Object' as the Type-Argument, but the thing is I WANT TO DECLARE MY DELEGATE INSTANCE TYPE-FREE, that is with that T in there, and later specify it in the Call-Sight, exactly as i demonstrated, not to dodge the problem by declaring the 'Action' with 'Object' Type-Argument.

    So far C#'s syntax capabilities have shown that this is possible to express - the C# syntax has the required complexity to express this requirement - but it would generete errors unless put in a method which has a Type-Argument:

    public void Method1<T>()
    {
        Action<T> action1 = x => {};
    }

    What i want to say is that, this should be possible to do, perhaps C#-Compiler should understand this because the langueage (i.e. C#) has the required complexity to express this need.

    And what do you want to do with your delegate?  If the parameter could be anything, then the body of the delegate can only perform operations on it that could be applied on any object.  That means that what I suggested before, using "Action<object>", would be perfectly appropriate.  If you want to do something else you'll need to be much more specific about what you actually want to do.  You want to create an action with a generic parameter as it's parameter, that's easy, as you've demonstrated with that code there.  Actually getting that to do something useful is another matter entirely.  

    If you don't actually tell us what you *really* want to do then you won't be able to get any more help then you've already been given.

    Wednesday, October 24, 2012 1:57 PM
  • Consider the dynamic keyword as well, for example:

        Action< dynamic > action1 = parameter1 => Console.WriteLine( parameter1.ToString( "G" ) );

    You can call it for any type that implements this variant of ToString, for example int or double:

        int x = 1;

        double y = 2.5;

        action1(x);

        action1(y);

    If you call it for other objects, you receive a runtime error. If you try Action<object>, you get a compiler error.

    • Edited by Viorel_MVP Friday, October 26, 2012 6:19 AM
    Friday, October 26, 2012 6:14 AM
  • Dear Mike Danes,

    Great Answer! The two points you made are JUST the reasons why this is IMPOSSIBLE.

    The fact is that it is within Notion that C#-Compiler understands and figures out the Type-Argument required; the propeller of this theory is a simple Generic method that can be implemented without specifying the Type-Argument within its signature, but when called in another method, is type specific:

    public class Sample1
    {
        public void Method1()
        {
            Method2<int>();
    
            Method2<string>();
    
            Method2<Sample1>();
        }
    
        public void Method2<T>()
        {
            // some operations.
        }
    }

    In the sample above it is demonstrated that a simple generic method (Method2) can be written with unspecified Type-Argument, and in the Call-Sight the caller would call Method2 with whatever type he likes. This is as you mentioned a Compile-Time feature.

    public class Sample2
    {
        public void Method1()
        {
            Action<T> action1 = x => { };
    
            action1<int>();
    
            action1<string>();
    
            action1<Sample2>();
        }
    }

    But here comes the problem in Sample2, in which it is not possible to declare an Action delegate without specifyting its Type-Argument. Now i thoroughly understand that this is due to differences between delegates and methods, but my question is why C#-Compiler should not understand this when it can simply understand the other?!
    • Edited by Kiarash.Nakhaei Friday, October 26, 2012 3:31 PM grammar checks
    Friday, October 26, 2012 3:27 PM
  • When defining a delegate, just as when you're defining a method, you can supply a generic argument:

    public class MyClass //note not generic
    {
        public delegate void MyAction<T>(T param);
    }

    When you create an actual instance of that delegate you need to provide an actual type for the generic arguments, just like when calling a generic method you need to provide an actual type.

    In any case, it seems you don't have any actual use case here; there is no compelling reason you have to *want* to do this.  Rather than asking yourself, "why doesn't the language let me do this?" you must first ask yourself, "why should the language let me do this?".  Language features are, by default, unimplemented.  They didn't go through every single possible feature and choose to not implement most of them.

    Now, given that, what you're wanting to do really isn't possible anyway.  If you want to define a variable of type "Action" but without specifying the value of the generic constraint then what could you possibly assign to that variable?  The entire point of a delegate is to hold onto a reference to a function (and possibly an instance of the type the function is defined in) with a Known signature.  That "known signature" is *very* important.  An "Action<int>" is a reference to a function that takes an int and returns void, an "Action<string>" is a reference to a function that takes a string and returns void.  What is an "Action<T>"  It's a function that takes an unknown parameter and returns nothing.  Well, there are no functions that take an unknown parameter, all functions in C# must take *something* as a parameter, so you couldn't ever assign anything to that action.

    Friday, October 26, 2012 3:45 PM
  • "but my question is why C#-Compiler should not understand this when it can simply understand the other?!"

    And my question is "why should it support this?"

    I'm pretty sure that one could design a language similar to C# and that has such a feature. But in doing so one needs to decide how to implement such a thing.

    One solution would be to treat variables like action1 in a special way: it wouldn't be an actual variable, one that exists at runtime. It would be just some kind of symbol that is associated with a generic anonymous method and would allow you to call that method. Something similar to "using foo = somenamespace.bar".

    At this point it becomes a confusing feature, it looks like a variable but it's not a variable. It has a delegate type but it doesn't create any delegate instances and in fact doesn't exist at runtime at all.

    Perhaps a syntax that at least doesn't involve delegates would be more appropriate:

    let action1<T> = x => {};

    action1<int>();

    Even so you still have to answer the main question, what for?

    Friday, October 26, 2012 3:57 PM
  • Hello;)

    First please cancel marking one of my answers as "abuse", because we don't suggest users using totally upper words, which isn't very polite to others.

    Who marks our issues?!

    Then come to your question——I don't know what you really want……?"T" is a generic type, you don't need to re-define that.


    我的博客园
    慈善点击,点击此处
    和谐拯救危机,全集下载,净化人心

    Saturday, October 27, 2012 3:36 AM