Simon Karman
Simon Karman

Reputation: 144

Using params in a Delegate as a base class

I have a delegate expecting parameters of type A as parameters. So A is the base class. Class B and C inherit from A.

The problem is that although B and C inherit from the base class A, the DoSomething functions at the bottom of the script can't be converted to the delegate.

public class A { }
public class B : A { }
public class C : A { }

public delegate void CallbackAction(params A[] paremeters);
public class Main
{
    public int main(params string[] args)
    {
        CallbackAction callbackAction;
        callbackAction = DoSomething1;
        callbackAction = DoSomething2;
        callbackAction = DoSomething3;

        return 0;
    }

    public void DoSomething1(A arg0) { }
    public void DoSomething2(B arg0) { }
    public void DoSomething3(C arg0) { }
}

Is there any way to use params in a delegate and be able to use classes that have the params class as their base class?

When compiling the error I get is: Error 5 No overload for 'DoSomething3' matches delegate 'SKConsole.CallbackAction'

I'm using .NET 4 and XNA

EDIT:: Ok let me explain why I am using this I am creating a console. This means a programmer using my console can add a command (console.AddCommand("help", Help) to the console, Help here is a function. When you are ingame and typing help in the console it will execute the function Help(). I now want it to work with console.AddCommand("setSpeed", SetPlayerSpeed) aswell. The SetPlayerSpeed function has 1 parameter, an int. But I want it to work with any function so if a programmer creates the function DoSomeFancyStuff(float a, string b, int c) I want the console to create a command and if you type in the correct string in the console execute these command.

I already tried making lots of delegates for different functions, but this is kinda ugly in my opinion.

What I then tried was the following

public abstract class SKConsoleParameter
{
    protected string value;

    public SKConsoleParameter(string value)
    {
        this.value = value;
    }
    public string GetRawValue()
    {
        return value;
    }

    public abstract bool IsValid();
    public abstract object GetValue();
}

public class StringParam : SKConsoleParameter
{
    public StringParam(string value) : base(value) { }

    public override bool IsValid()
    {
        return true;
    }

    public override object GetValue()
    {
        return value;
    }
}

public class IntParam : SKConsoleParameter
{
    public IntParam(string value) : base(value) { }

    public override bool IsValid()
    {
        int i;
        return int.TryParse(value, out i);
    }

    public override object GetValue()
    {
        int i;
        if (int.TryParse(value, out i))
            return i;
        else
            return 0;
    }
}

Was this does is that if a developer creates a function like:

DoSomethingCool(StringParam s, IntParam i)

Then it can receive the values by using (string)s.GetValue() and (int)i.GetValue() The StringParam and IntParam classes both inherit from SKConsoleParameter, so i though I could now create the follwoing delegate

void CoolDelegate(params SKConsoleParameter[] parameters)

But this doesn't work.. Because of the abstract problem with class A, B and C at the top of this page

Does anyone have any ideas to counter this problem?

Upvotes: 1

Views: 1206

Answers (3)

Glenn Ferrie
Glenn Ferrie

Reputation: 10390

based on your updated code -- try this. As long as your method matches the delegate the contravariance should work as expected in .NET 3.5 or better

public abstract class SKConsoleParameter
{
    protected string value;

    public SKConsoleParameter(string value)
    {
        this.value = value;
    }
    public string GetRawValue()
    {
        return value;
    }

    public abstract bool IsValid();
    public abstract object GetValue();
}

public class StringParam : SKConsoleParameter
{
    public StringParam(string value) : base(value) { }

    public override bool IsValid()
    {
        return true;
    }

    public override object GetValue()
    {
        return value;
    }
}

public class IntParam : SKConsoleParameter
{
    public IntParam(string value) : base(value) { }
    public override bool IsValid()
    {
        int i;
        return int.TryParse(value, out i);
    }
    public override object GetValue()
    {
        int i;
        if (int.TryParse(value, out i))
            return i;
        else
            return 0;
    }
}   

class Program
{

    public delegate void CoolDelegate(params SKConsoleParameter[] parameters);

    static void Main(string[] args)
    {
        var s = new StringParam("Glenn");
        var i = new IntParam("12");
        var coolDel = new CoolDelegate(DoSomethingCool);
        coolDel(s, i);
    }

    public static void DoSomethingCool(params SKConsoleParameter[] parameters)
    {
        if (parameters == null) throw new ArgumentNullException("parameters");
        foreach (var item in parameters)
        {
            if (item is IntParam)
            {
                // do something interesting
                continue;
            }

            if (item is StringParam)
            {
                // do something else interesting
                continue;
            }

            throw new NotImplementedException("unknown param type");
        }
    }
}

Upvotes: 0

Anton Tykhyy
Anton Tykhyy

Reputation: 20066

You are looking for the error in the wrong place. C# allows contravariance in delegates' input parameters. The problem with your code is that your delegate takes params A[], while your method takes a single A. This is not allowed. Delcare your delegate as accepting a single A:

delegate void Callback1 (B a) ;
void Test11 (A a) {}
void Test12 (B b) {}

Callback1 c11 = Test11 ; // OK
Callback1 c12 = Test12 ; // OK

Note also that this does not work with array parameters:

delegate void Callback2 (B[] a) ;
void Test21 (A[] a) {}
void Test22 (B[] b) {}

Callback2 c21 = Test21 ; // compile error
Callback2 c22 = Test22 ; // OK

Upvotes: 1

Glenn Ferrie
Glenn Ferrie

Reputation: 10390

Try this code:

 public class A {  }
public class B : A { }
public class C : A { }

public static class Helper
{
    public static Action<A> DoSomething;
}

class Program
{
    static void Main(string[] args)
    {
        var a = new A();
        var b = new B();
        var c = new C();

        Helper.DoSomething = new Action<A>(DoSomething1);
        Helper.DoSomething = (Action<A>)new Action<B>(DoSomething2);
        Helper.DoSomething = (Action<A>)new Action<C>(DoSomething3);                       
    }

    public static void DoSomething1(A a) { }
    public static void DoSomething2(B a) { }
    public static void DoSomething3(C a) { }
}

Upvotes: 0

Related Questions