-3

I am refactoring an old library with some class like the following one:

public class Template  //Old one
{
    public double Value { get; set; }
}

Now it's better to provide more flexibility to allow user defined Value type. So I changed Template to a generic class:

public class Template<T>
{
    public T Value { get; set; }
}

This change breaks the usages of old Template class from others, so I tried to add backward compatibility (allows others to use my library in the old way) with:

public class Template: Template<double> { }

But it also requires lots of changes in the library. Especially where the old Template is used, such as:

public class AnotherClassInLibrary<T>
{
    public Template<T> API() {...}
}

public class AnotherClassInLibrary : AnotherClassInLibrary<double>
{
    // This class is also defined for the old way of using the library
    public Template API()
    {
         return base.API();
         // Error here: Template<double> cannot be implicitly convert to Template.
    }
}

So here comes the problem: the new Template is inherited from Template<double>, so casting is not a good/working practice here. Is there a work around to keep AnotherClassInLibrary backward compatible while not rewrite the API() code twice?

P.S. I really hope C# has something like typedef in C++. But the answer is NO.

Moony Chou
  • 497
  • 5
  • 10
  • 1
    Where is your non-generic `Template` class? – rory.ap Aug 20 '18 at 14:42
  • what if another type implemented `Template`, would you still want that cast to be valid? – Selman Genç Aug 20 '18 at 14:43
  • 6
    _"Though `Template` is exactly a `Template`"_ - no it's not, it's one class deeper into the inheritance chain. – CodeCaster Aug 20 '18 at 14:43
  • You are trying to cast from a parent to a child. What if you had `Template a`? Do you still want to be able to cast it? – Code-Apprentice Aug 20 '18 at 14:46
  • @rory.ap The original code is quite complex so I tried to put simplified code here. You can think the original Template class as: public class Template { public double Value { get; set; } } – Moony Chou Aug 20 '18 at 14:47
  • For this to work `Template` would need to inherit from whatever `Template` is. – juharr Aug 20 '18 at 14:47
  • 1
    Just because you supply no new functionality, that still doesn't make `Template` some kind of *synonym* or *alias* for the type `Template`. They're distinct types. – Damien_The_Unbeliever Aug 20 '18 at 14:49
  • @Code-Apprentice No I don't. I just want to add backward compatibility to only double type. – Moony Chou Aug 20 '18 at 14:53
  • @MoonyChou It is backwards compatible; you can always implicitly convert a type to its base types. You want it to be forwards compatible. That doesn't work. You can't treat something as a more derived type than it is. – Servy Aug 20 '18 at 14:55
  • @SelmanGenç Good question! I think the answer is yes for my purpose. In my case I just want to convert a base template class to a corresponding inherited class. – Moony Chou Aug 20 '18 at 15:00
  • @CodeCaster You are exactly right in C# programming language logic :D – Moony Chou Aug 20 '18 at 15:01
  • @DavidG I can modify the old Template class as much as I want. But it's hard for me to change the code that uses the old Template public members. – Moony Chou Aug 20 '18 at 15:03
  • @Damien_The_Unbeliever Yes you are right. I've just rewrite my question to demonstrate why I did all these weird things... – Moony Chou Aug 20 '18 at 15:33
  • Have you considered having an `ITemplate` *interface* that both `Template` and `Template` implement (and no inheritance between the classes). Difficult to know if it'll help without knowing your use cases. – Damien_The_Unbeliever Aug 20 '18 at 16:03
  • @Damien_The_Unbeliever It also will require changing of the exisiting code so it'll use the interface. If he did think it previously - yes, but if no... – Chayim Friedman Aug 20 '18 at 16:18

1 Answers1

0

Even though your Template class inherits from Template<double>, it's not the same. Conside you will change the defintion of Template as follows:

public class Template : Template<double>
{
    public int AddedMember { get; set; }
}

Now, you try to execute the previous code. You're trying to convert a class with some properties, to a class with more properties - you'll access to incorect places in the memory!

You always can convert a class to its base class, because the drived class contains all the base class' memebers. So, explicit conversion is not needed. However, when you're trying to convert a base class to a drived class, the convert will success only if the base class variable points to an instance of the drived class, and so, all the memebers of the drived class are exist. If no, an InvalidCastException will thrown at runtime. So, explicit cast is required (because one of the guidlines why to use an explicit cast is to use an explicit cast if the cast may fail).

So, if you'll change your code so the variable a (of type Template<double>), the convert will success:

Template<double> a = new Template();
Template b = (Template)a; // No exception will thrown

The last code will success beacuse the variable a points to an instance of Template (and not of Template<double>), so we sure (at runtime) that all the members of Template is exist and no error will occur.

Edit:

After you said your requirements, I can help you. What you want? you want to enable convert of `Template` to `Template` - this is possible with [user-defined conversions](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/conversion-operators).

But, custom conversions between drived class and base class are not allowed. So, there are two solutions:

  1. Template won't inherit from Template<double>, as follows:
    public class Template<T>
    {
        public T Value { get; set; }
    }
    
    public class Template
    {
        public double Value { get; set; }
        
        public static explicit operator Template(Template<double> generic) // Or implicit instead of explicit
        {
            return new Template { Value = generic.Value };
        }
    }

    var generic = new Template<double> { Value = 1234.56 };
    var nongeneric = (Template)generic; // Or Template nongeneric = generic; if the conversion defined as implicit
  1. You won't define a custom conversion, and instead, you'll define a regular method (I defined a constructor):
    public class Template<T>
    {
        public T Value { get; set; }
    }
    
    public class Template : Template<double>
    {
        public Template(Template<double> generic)
        {
            this.Value = generic.Value;
        }
    }
    
    var generic = new Template<double> { Value = 1234.56 };
    var nongeneric = new Template(generic);
Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77
  • Thank you for the answer. There are many shared methods between Template and Template, so I chose the second solution yesterday after many trials. Hope your answer would help others with similar questions :D – Moony Chou Aug 21 '18 at 12:21