简体   繁体   中英

Polymorphism, overloads and generics in C#

class Poly
    {
    public static void WriteVal(int i) { System.Console.Write("{0}\n", i); }
    public static void WriteVal(string s) { System.Console.Write("{0}\n", s); }
    }

class GenWriter<T>
    {
        public static void Write(T x) { Poly.WriteVal(x); }
    }

Why the innocent (for C++ programmer) method Write is not acceptable in C#?

You can see that the compiler tries to match the parameter type T to concrete overloads instantiation: 实例化尝试将参数类型T与具体重载匹配:

Error 3 The best overloaded method match for 'TestGenericPolyMorph.Poly.WriteVal(int)' has some invalid arguments

Of course. the purpose was not to use the static method as above, the intention is to create a wrapper with polymorphic behavior. Note: I use VS 2010.

Please, note that all the needed information is available in compile time. Once again: the problem is that the validation is performed before the template instantiation.

Addition after the discussion:

Well, may be I have not stressed this out properly. The question was not only about the difference between generics and templates, but also about solution of the following problem: given set of overloads addressing different types, I want to generate set of wrapper classes providing virtual method (polymorphism) for these types. The price of resolution of virtual methods in run-time is minimal and does not hit performance. This is where C++ templates were handy. Obviously, the overhead of the run-time type resolution for dynamic is quite different. So, the question is whether one can convert existing overloads to polymorphism without replication of the code and without paying the performance penalty (eg, I am not sure what I gain with dynamic compared to "switch" attempting to cast except of nicer syntax).

One of the solutions I have seen so far was to generate/emit code (sic!), ie instead of cut-and-paste to do this automatically.

So, instead of the C++ template processing we simply do it manually or just re-invent macro/template processor.

Anything better?

Short answer:

C# generics are not C++ templates; despite their similar syntax they are quite different. Templates are built at compile time, once per instantiation, and the templatized code must be correct for only the template arguments actually provided . Templates do tasks like overload resolution and type analysis once per instantiation; they are basically a smart "search and replace" mechanism on source code text .

C# generics are truly generic types; they must be correct for any possible type argument . The generic code is analyzed once , overload resolution is done once , and so on.

Long answer: This is a duplicate of

What are the differences between Generics in C# and Java... and Templates in C++?

See the long answers there for details.

See also my article on the subject:

http://blogs.msdn.com/b/ericlippert/archive/2009/07/30/generics-are-not-templates.aspx

为什么你不能简单地写:

public static void Write<T>(T x) { System.Console.Write("{0}\n", x); }

The C++ and C# generics are different ( http://msdn.microsoft.com/en-us/library/c6cyy67b(v=VS.80).aspx , search for "c# c++ generics difference" on your favorite search site)

Short: C# compiler must create complete GenWriter<T> class with all type matching by just looking at the class itself. So it does not know if T will only be int/string or any other type.

C++ compiler creates actual class by looking at instantiation of the generic GenWriter<int> and declaration GenWriter<T> and then creates class for that particular instance.

If someone was to call GenWriter(5.0) , this would be inferred to GenWriter<double>(5.0) , and the method call inside Write(T x) would become:

public static void Write(double x) { Poly.WriteVal(x); }

There is no overload of WriteVal which takes a double. The compiler is informing you that there are no valid overloads of WriteVal .

C# generics and C++ templates are not entirely equivalent.

You can't do that in C# because the compiler doesn't know what is the type of x in compile time.

Without knowledge of T's actual type, the compiler is concerned that you might have intended to perform a custom conversion. The simplest solution is to use the as operator, which is unamibigous because it cannot perform a custom conversion.

A more gereral solution is to cast to object first. This is helpfull because of boxing unboxing issues:

return (int)(object) x;

Have in mind that C# Generics are not like C++ templates. C++ templates are pieces of code that compile for each type separately. While C# generics are compiled in an assebmly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM