C # extension compilation / crash compatibility method based on namespace order

I am writing two extension methods. One for working on one object, and the other for working with a collection of objects. When calling the extension method, the C # compiler seems to get confused as to which one to use, and does not compile.

More surprisingly, if you move extension methods to different namespaces, even if I include both namespaces in callsite, compilation will fail if the namespaces are in a certain order alphabetically - switching namespaces will result in successful compilation.

Here is the code:

public static class DBObjectExtensions { public static void PopulateRelations<T>(this T obj, params RelationToPrefetch[] relationsToPrefetch) where T : IDBObject { if (obj == null) { return; } obj.Transaction.PopulateRelations<T>(new[]{ obj }, relationsToPrefetch); } public static void PopulateRelations<T>(this IEnumerable<T> objects, params RelationToPrefetch[] relationsToPrefetch) where T : IDBObject { var first = objects.FirstOrDefault(); if (first == null) { return; } first.Transaction.PopulateRelations<T>(objects, relationsToPrefetch); } } 

This is a call line that does not compile:

 List<ITable> list = ... // ITable inherits from IDBObject list.PopulateRelations(xxx); 

Error with error CS0311:

The type "System.Collections.Generic.List" cannot be used as a parameter of type "T" in the generic type or method "Granta.MI.DBObjectExtensions.PopulateRelations (T, params Granta.MI.RelationToPrefetch []) ', There is no implicit conversion links from "System.Collections.Generic.List" to "Granta.MI.IDBObject".

Note that this line compiles successfully if I remove the second extension method.

Also note that using trampoline methods (for every possible type of collection ...) also works:

 public static void PopulateRelations<T>(this List<T> objects, params RelationToPrefetch[] relationsToPrefetch) where T : IDBObject { ((IEnumerable<T>)objects).PopulateRelations(relationsToPrefetch); } public static void PopulateRelations<T>(this IList<T> objects, params RelationToPrefetch[] relationsToPrefetch) where T : IDBObject { ((IEnumerable<T>)objects).PopulateRelations(relationsToPrefetch); } 

Why can't the compiler find a suitable extension method? And even more vaguely, if I put one of the methods in a different namespace and I include this namespace, why will the compilation succeed? Is there anything I can do to fix this?

+5
source share
2 answers

General restrictions are not part of the method signature, so the compiler chooses PopulateRelations<T>(this T obj, params RelationToPrefetch[] relationsToPrefetch) because T is more derived than IEnumerable<T> .

Example: between these two methods:

 public static void PopulateRelations(this List<ITable> obj, params RelationToPrefetch[] relationsToPrefetch) { // Do something } public static void PopulateRelations(this IEnumerable<ITable> objects, params RelationToPrefetch[] relationsToPrefetch) { // Do something } 

The first is selected upon invocation:

 List<ITable> list; PopulateRelations(list, something); // Not calling as extension method to more clear 

Because list matches directly with List<ITable>

+2
source

Your generic types are limited by IDBObject, so you can just make extension methods nonequivalent:

 public static void PopulateRelations(this IDBObject obj, params RelationToPrefetch[] relationsToPrefetch) { //... } public static void PopulateRelations(this IEnumerable<IDBObject> objects, params RelationToPrefetch[] relationsToPrefetch) { //... } 

It solves a compilation error.

0
source

Source: https://habr.com/ru/post/1232106/


All Articles