Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# - Passing all the method arguments to another method?

Tags:

c#

oop

I've got a class, which has three overloaded methods. Let's say, there is:

class MyChildClass
{
    public void myMethod(int i)
        { /* do something with i */ }
    public void myMethod(int a, string b)
        { /* get i from a and b and call: */ myMethod(i); }
    public void myMethod(string c, string d)
        { /* get i from c and d and call: */ myMethod(i); }
}

Now I'd like this class to be a private field in other (parent) class, but I need those three methods to be accessible. For now, I just did:

class MyBaseClass
{
    private MyChildClass myObject = new myChildClass(); // or similar
    public void myMethod(int i) 
        { myObject.myMethod(i);    }
    public void myMethod(int a, string b) 
        { myObject.myMethod(a, b); }
    public void myMethod(string c, string s) 
        { myObject.myMethod(c, d); }
}

Is there a way to implement it as one short method? Something which would look like:

public void myMethod(unknownListOfArgumentsOfDifferentTypes args)
    { myObject.myMethod(args); }

I tried to use public void myMethod(params object[] something) but it didn't work. Is it possible, or do I have to "project" every method into another?

EDIT: Child class has various methods and fields, which I want to be accessible for parent class only. That's why I don't want parent to derive after it. I didn't explain that, sorry if it looked like child class contains only those three methods. Those are the methods I want to be accessible as public methods of parent class.

like image 950
WRonX Avatar asked Oct 06 '22 16:10

WRonX


2 Answers

Why don't you do

class MyChildClass : MyBaseClass
{
}

same effect, less code, and this way MyChildClass is a MyBaseClass


If you implement some sort of generic facade using reflection you'll just be reducing performance, bypassing the benefits of type safety and delaying the discovery of problems.

You'll also have have a "has a" relationship instead "is a" relationship which is incongruent with your class names.


If you want to give up this simplicty with its associated benefits you could make use the GetMethodBySig extension accepted in this post.

Something like this,

class SemiGenericFacade<T> where T : new()
{
    private readonly t = new T();

    public void CallVoidOnT(string name, params object[] parameters)
    {
        var paramTypes = parameters.Select(p => typeof(p))

        var match = typeof(T).GetMethodBySig(typeof(void), paramTypes)
            .Single(mi => mi.Name == name);

        match.Invoke(this.t, parameters);
    }
}

Following on from Piotr Justyna's comment, implementing and using this method results in the cat turning into a tiger and eating her kittens.


If you were to do this it would make sense to add to the linked extension

public static class Extensions
{
    public static MethodInfo GetMethodByNameThenSig(
        this Type type,
        string name, 
        Type returnType, 
        params Type[] parameterTypes)
    {
        return type.GetMethods().Where((m) =>
        {
            if (m.Name != name)
            {
                return false;
            }

            if (m.ReturnType != returnType)
            {
                return false;
            }

            var parameters = m.GetParameters();
            if ((parameterTypes == null || parameterTypes.Length == 0))
            {
                return parameters.Length == 0;
            }

            if (parameters.Length != parameterTypes.Length)
            {
                return false;
            }

            for (int i = 0; i < parameterTypes.Length; i++)
            {
                if (parameters[i].ParameterType != parameterTypes[i])
                {
                    return false;
                }
            }

            return true;
        }).Single();
    }
}

Which you could use like this,

class GenericFacade<T> where T : new()
{
    private readonly t = new T();

    public void CallOnInternal(string name, params object[] parameters)
    {
        var paramTypes = parameters.Select(p => typeof(p))

        var match = typeof(T).GetMethodByNameThenSig(
            name,
            typeof(void),
            paramTypes);

        match.Invoke(this.t, parameters);
    }

    public TResult CallOnInternal<TResult>(string name, params object[] parameters)
    {
        var paramTypes = parameters.Select(p => typeof(p))

        var match = typeof(T).GetMethodByNameThenSig(
            name,
            typeof(TResult),
            paramTypes);

        return (TResult)match.Invoke(this.t, parameters);
    }
}

FINAL EDIT

Looking at the code involved to use reflection and considering the cost associated with the loss of type safety. I'd suggest its better to establish the "has-a" relationship explicitly in the traditional manner.

like image 186
Jodrell Avatar answered Oct 10 '22 01:10

Jodrell


You can use public void myMethod(params object[] something) as in:

public static void Main()
{
    UnknownArgumentsMethod1(1, 2, 3, "foo");
}

public static void UnknownArgumentsMethod1(params object[] list)
{
    UnknownArgumentsMethod2(list);
}

public static void UnknownArgumentsMethod2(params object[] list)
{
    foreach (object o in list)
    {
        if (o.GetType() == typeof(int))
        {
            Console.WriteLine("This is an integer: " + (int)o);
        }
        else if (o.GetType() == typeof(string))
        {
            Console.WriteLine("This is a string: " + (string)o);
        }
    }
}
like image 31
sohil Avatar answered Oct 10 '22 02:10

sohil