Implicit typing of arrays that implement interface

2019-04-21 09:27发布

问题:

I was under the impression that the C# compiler will implicitly type an array based off a type that they can all be implicitly converted to.

The compiler generates No best type found for implicitly-typed array

public interface ISomething {}

public interface ISomething2 {}

public interface ISomething3 {}

public class Foo : ISomething { }
public class Bar : ISomething, ISomething2 { }
public class Car : ISomething, ISomething3 { }

void Main()
{
    var obj1 = new Foo();
    var obj2 = new Bar();
    var obj3 = new Car();

    var objects= new [] { obj1, obj2, obj3 };
}

I know that the way to correct this is to declare the type like:

new ISomething [] { obj1, ...} 

But I'm after an under the covers type help here.

回答1:

The C# compiler considers the set of types of all the specified elements. It does not consider common base types etc.

You could cast one of the expressions:

var objects= new [] { obj1, obj2, (ISomething) obj3 };

... but personally I'd just use the explicit form:

var objects= new ISomething[] { obj1, obj2, obj3 };

Alternatively, if you explicitly declared any or all of obj1, obj2 and obj3 as type ISomething, that would work fine too without changing the array initialization expression.

From the C# 3 spec, section 7.5.10.4:

An array creation expression of the third form is referred to as an implicitly typed array creation expression. It is similar to the second form, except that the element type of the array is not explicitly given, but determined as the best common type (§7.4.2.13) of the set of expressions in the array initializer.

Section 7.4.2.13 looks like this:

In some cases, a common type needs to be inferred for a set of expressions. In particular, the element types of implicitly typed arrays and the return types of anonymous functions with block bodies are found in this way. Intuitively, given a set of expressions E1…Em this inference should be equivalent to calling a method

Tr M<X>(X x1 … X xm)

with the Ei as arguments. More precisely, the inference starts out with an unfixed type variable X. Output type inferences are then made from each Ei with type X. Finally, X is fixed and the resulting type S is the resulting common type for the expressions.



回答2:

If the instances can all be cast to the type of any one instance, than that type will be used. It's not enough for all instances to have any type in common, or else the implicity array initialization would always succeed and often generate undesired new object[] arrays.



回答3:

As a slight addition to the Skeet's reply:

You can either cast one of the array items to the type you need (interface in this case) or if you had just a single element of that type (not deriving but of a direct type). Such as

public static IWindsorInstaller[] MobileRestComponentInstallers
        {
            get
            {
                return new []
                           {
                                 new RepositoryInstaller(),
                                 new AppSettingsInstaller(),
                                 // tens of other installers...
                                 GetLoggerInstaller() // public IWindsorInstaller GetLoggerInstaller()...
                           };
            }
        }

this will work, but pls don't do that :) Just define the array type and change the new[] to new IWindsorinstaller[]. It's much more readable having the array type defined explicitly.



回答4:

Do like this for Class object( UIViewController) initialization in var array:



 var page1 = new Class1();
 var page2 = new Class2();
 var pages = new UIViewController[] { page1, page2 };

Note: here UIViewController can be any class