1

What I want to do is the following:

  1. Have several classes, one of which is a base one for the rest. All those classes are not self-made.
  2. Have a function overridden for each of the classes including the base one.
  3. Have a single generic function that uses the set of overridden functions. The generic function constrains its type parameter to be based on the base class.

Here's the code:

public class Program
{
    class Base
    {
    }

    class A : Base
    {
    }

    class B : Base
    {
    }

    private static int Id(Base obj)
    {
        return 0;
    }

    private static int Id(A obj)
    {
        return 1;
    }

    private static int Id(B obj)
    {
        return 2;
    }

    private static int GetId<T>(T obj) where T : Base
    {
        return Id(obj);
    }

    public static void Main(string[] args)
    {
        int idBase = GetId(new Base());
        int idA = GetId(new A());
        int idB = GetId(new B());
        // expected    | idBase=0, idA=1, idB=2
        // but getting | idBase=0, idA=0, idB=0
        Console.WriteLine($"idBase={idBase}, idA={idA}, idB={idB}");
    }
}

Since C# doesn't support explicit specialization I cannot use a template function and specialize it for each of the instances, but probably it is needless to use such since a set of overridden such would do even better work. Yet, the correct overrides are not called but only the one for the base class. Even the IDE is giving me hints that the rest are not used. Tell me what I am missing.

Ivan Caravanio
  • 597
  • 1
  • 7
  • 20
  • 1
    [Related](https://stackoverflow.com/questions/62114504/call-overloaded-generic-method-from-generic-method/62114675#62114675). Not sure if it's a duplicate. – Sweeper Jun 02 '20 at 13:55
  • 1
    First of all what you're trying to do is not called overwriting but overloading. Secondly why would you assume it would call the run-time correct overloads? It has to compile something, all it knows is that whatever it gets looks like a `Base` so the best match that can be called with anything it receives is `private static int Id(Base obj)`. –  Jun 02 '20 at 13:57
  • You can't add things to `A`, `B` or `Base`, can you? – Sweeper Jun 02 '20 at 13:57
  • @Knoop Overloading for sure. – Ivan Caravanio Jun 02 '20 at 13:59
  • @Sweeper Exactly. As mentioned these are not self-made ones but foreign. – Ivan Caravanio Jun 02 '20 at 13:59
  • Well, then does my linked answer answer your question? In that answer I also provided a workaround. If it does, I'll close this as a dupe. If not, please explain why not. – Sweeper Jun 02 '20 at 14:00
  • What's the point of `static int GetId(T obj) where T : Base` anyway? Why not just go with `Id` and let overload resolution run it's course? – Zohar Peled Jun 02 '20 at 14:01
  • See [rextester demo](https://rextester.com/WCHQ73467) – Zohar Peled Jun 02 '20 at 14:03
  • @ZoharPeled I think OP just wants to resolve the overloads at runtime, which as I pointed out in the linked answer, can be done with `dynamic`, but since we know so little, what you suggest could be what the OP really needs as well. – Sweeper Jun 02 '20 at 14:06
  • @ZoharPeled Concentrate on the use-case. I have provided a simplified version of the real-world situation as you might guess. There's no ```GetId``` or ```Id``` in the real scenario and ```GetId``` real equivalent does something completely different from that of ```Id```'s one thus the former cannot be skipped just because ```Id``` does the same work. If you can, propose a way to cope with the use case - overloaded function handled by a generic one. – Ivan Caravanio Jun 02 '20 at 14:10
  • @Ivan It's not my fault you've over-simplified your code to the point the problem becomes redundant. Don't get me wrong, producing an good [mcve] can be very tricky and I do appreciate the effort you've made - but as I said, you've over-simplified the code. – Zohar Peled Jun 02 '20 at 14:17
  • @Sweeper For sure it is the same question. The only thing being is that your case describes a use case where generic calls a set of overloads of a function one of which is itself a generic. You can close it. It seems like ```dynamic``` is the way to go, which is strange because [C++ handles this](https://paiza.io/projects/mSA0VRGM9GXnf8IS2WInHQ?language=cpp) nice and easy. – Ivan Caravanio Jun 02 '20 at 14:48

1 Answers1

0

You can do this by using a generic type object, from there you can obtain the underlying type using the is keyword:

public class Program
{
    class Base
    {
    }

    class A : Base
    {
    }

    class B : Base
    {
    }

    private static int Id(object obj)
    {
        if (obj is A) return 1;
        if (obj is B) return 2;
        return 0;
    }

    private static int GetId<T>(T obj) where T : Base
    {
        return Id(obj);
    }

    public static void Main(string[] args)
    {
        int idBase = GetId(new Base());
        int idA = GetId(new A());
        int idB = GetId(new B());
        Console.WriteLine($"idBase={idBase}, idA={idA}, idB={idB}");
    }
}
BsdDaemon
  • 26
  • 2
  • This is the last stop approach that for sure "works" but is the least preferred one in terms of code correctness - ruins inheritance. – Ivan Caravanio Jun 02 '20 at 14:13
  • I agree, but with some PropertyInfo magic you can get those underlying values back: https://stackoverflow.com/questions/4144778/get-properties-and-values-from-unknown-object – BsdDaemon Jun 02 '20 at 14:22
  • I am sure there are technical possibilities to achieve the desired result, which I also thought of but there should be good coding practices approach that solves the scenario cleanly. If there isn't then it is best that I rethink the separation of concerns and design. Actually, [this works on C++](https://paiza.io/projects/mSA0VRGM9GXnf8IS2WInHQ?language=cpp) flawlessly. – Ivan Caravanio Jun 02 '20 at 14:52
  • I think this is a limitation of C#, if you know C++ it might be best to convert the project to VC++ in Visual Studio. Unfortunately garbage collected languages will always have limitations due to layers of abstraction, such as the inability to use pointers without the unsafe keyword. I always use C#, but I have found myself bringing some of my projects to C++, sometimes you really need the full toolkit. – BsdDaemon Jun 02 '20 at 14:59
  • You can also separate the business logic and design by having the PropertyInfo Magic and generic methods in an abstract class that you extend. It is not the best, but it might save you from rewriting in C++. – BsdDaemon Jun 02 '20 at 15:11