5argon
5argon

Reputation: 3863

Calling a generic method multiple times based on fixed list of type

I have a generic method Method<T>() and I have a list of types like below :

public static readonly Type[] AllPossibleTypes = new Type[]{
        typeof(A),
        typeof(B),
        typeof(C),
        typeof(D)
};

There are multiple place in my code that I would have to switch case and do pattern matching, then finally call each one manually e.g. Method<A> Method<B> Method<C> ... then when I decided to add more type to the list of possible type I would have to remember to fix many places in the code. Is there any approach to iterate through possible types and have it usable with generic? The list of type will not change at runtime.

A B C D implements the same interface and no other type implements that interface, if that would help me achieve this goal.

public interface IToIntCompatible
{
    int ToInt();
}

public struct A : IToIntCompatible
{
    public string data;
    public int ToInt() => data.ToArray().GetHashCode();
}

public struct B : IToIntCompatible
{
    public float data;
    public int ToInt() => (int)Math.Round(data);
}

public struct C : IToIntCompatible
{
    public bool data;
    public int ToInt() => -1;
}


public class Example
{
    /// <summary>
    /// I wish I can add type D in the future without having to touch PlaceA and PlaceB
    /// </summary>
    public static readonly Type[] allTypes = new Type[] { typeof(A), typeof(B), typeof(C) };

    /// <summary>
    /// Cannot modify this function.
    /// </summary>
    private void Save<T>(T data) where T : struct { }

    public void PlaceA(IToIntCompatible input)
    {
        switch (input)
        {
            case A a: Save<A>(a); break;
            case B b: Save<B>(b); break;
            case C c: Save<C>(c); break;
        }
    }

    public void PlaceB(IToIntCompatible input)
    {
        switch (input)
        {
            case A a: Save<A>(a); break;
            case B b: Save<B>(b); break;
            case C c: Save<C>(c); break;
        }
    }
}

Upvotes: 4

Views: 105

Answers (4)

Alpha75
Alpha75

Reputation: 2280

"A B C D implements the same interface and no other type implements that interface, if that would help me achieve this goal."

¿Has this interface implements your Method? If not you'll need to add this method to this interface or use a new one.

Edited with last update:

public interface IToIntCompatible
{
    int ToInt();
}

public interface ISaveCompatible
{
    void Save();
}

public struct A : IToIntCompatible, ISaveCompatible
{
    public string data;

    public void Save()
    {
        // save function for A type
    }

    public int ToInt() => data.ToArray().GetHashCode();
}

public struct B : IToIntCompatible, ISaveCompatible
{
    public float data;

    public void Save()
    {
        // save function for B type
    }

    public int ToInt() => (int)Math.Round(data);
}

public struct C : IToIntCompatible
{
    public bool data;
    public int ToInt() => -1;
}


public class Example
{
    public void Place(ISaveCompatible input)
    {
        input.Save();
    }
}

You don't need generics for this solution.

Upvotes: 0

Enigmativity
Enigmativity

Reputation: 117029

I think this does the job:

public class Example
{
    /// <summary>
    /// Cannot modify this function.
    /// </summary>
    private void Save<T>(T data) where T : struct { }

    public void PlaceA(IToIntCompatible input)
    {
        typeof(Example)
            .GetMethod("Save")
            .MakeGenericMethod(input.GetType())
            .Invoke(this, new object[] { input });
    }

    public void PlaceB(IToIntCompatible input)
    {
        typeof(Example)
            .GetMethod("Save")
            .MakeGenericMethod(input.GetType())
            .Invoke(this, new object[] { input });
    }
}

Upvotes: 1

Sanan Fataliyev
Sanan Fataliyev

Reputation: 630

You have to use reflection to call generic methods with dynamic choosen type argument.

typeof(Example).GetMethod("Save").MakeGeneric(input.GetType()).Invoke(this, new object[]{input});

Upvotes: 2

Jon Skeet
Jon Skeet

Reputation: 1500015

One option to consider is dynamic typing:

public void PlaceA(IToIntCompatible input)
{
    Save((dynamic) input);
}

That will perform generic type inference for you, as if you'd specified the actual execution-time type of input.

It's equivalent to writing the reflection-based code, but much simpler code. I'm not a big fan of using dynamic typing, but if you genuinely can't change anything else, it may be the simplest solution here.

Upvotes: 1

Related Questions