43 votes

Pourquoi n'y a-t-il pas un type implicite?

Je crois que je comprends pourquoi cette petite application console C# ne compile pas:

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void WriteFullName(Type t)
        {
            Console.WriteLine(t.FullName);
        }

        static void Main(string[] args)
        {
            WriteFullName(System.Text.Encoding);
        }
    }
}

Le compilateur soulève un CS0119 erreur: 'Encoding' is a type which is not valid in the given context. Je sais que je peux produire un Type d'objet à partir de son nom en utilisant l' typeof() opérateur:

        ...
        static void Main(string[] args)
        {
            WriteFullName(typeof(System.Text.Encoding));
        }
        ...

Et tout fonctionne comme prévu.

Mais pour moi, que l'utilisation d' typeof() a toujours semblé quelque peu redondant. Si le compilateur sait que certains token est une référence à un type donné (comme d'erreur CS0119 l'indique) et il sait que la destination de certains d'affectation (que ce soit un paramètre de la fonction, une variable ou quoi que ce soit) s'attend à une référence à un type donné, pourquoi ne peut pas le compilateur de le prendre comme un implicite typeof() appel?

Ou peut-être que le compilateur est parfaitement capable de prendre cette mesure, mais il a été choisi de ne pas en raison des problèmes que peuvent générer. Serait-ce le résultat de toute ambiguïté/questions de lisibilité, que je ne pense dès maintenant?

84voto

Eric Lippert Points 300275

Si le compilateur sait que certains token est une référence à un type donné (comme d'erreur CS0119 l'indique) et il sait que la destination de certains d'affectation (que ce soit un paramètre de la fonction, une variable ou quoi que ce soit) s'attend à une référence à un type donné, pourquoi ne peut pas le compilateur de le prendre comme un implicite typeof() appel?

Tout d'abord, votre proposition est que le compilateur raison à la fois "à l'intérieur à l'extérieur" et "l'extérieur vers l'intérieur" dans le même temps. C'est, pour faire de votre proposition de fonction, le compilateur doit à la fois en déduire que l'expression System.Text.Encoding fait référence à un type et que le contexte, un appel à WriteFullName -- nécessite un type. Comment savons-nous que le contexte n'exige une type? La résolution de l' WriteFullName nécessite la résolution de surcharge car il pourrait y avoir une centaine d'entre eux, et peut-être que l'un d'entre eux prend un Type comme un argument dans cette position.

Nous devons donc maintenant la surcharge de la conception de la résolution de reconnaître ce cas précis. Résolution de surcharge est assez dur déjà. Maintenant d'examiner les incidences sur l'inférence de type en tant que bien.

C# est conçu de telle sorte que, dans la grande majorité des cas, vous n'avez pas besoin de faire bidirectionnel inférence parce que bidirectionnel inférence est coûteux et difficile. L'endroit où nous faisons une utilisation bidirectionnelle inférence est lambdas, et il m'a pris la meilleure partie d'une année pour mettre en œuvre et de tester. L'obtention du contexte de l'inférence sur les lambdas a été un élément clé qui a été nécessaire pour faire LINQ travail et il a été vaut la très haute charge de la prise en bidirectionnel inférence de droit.

Par ailleurs: pourquoi est - Type de spécial? Il est parfaitement légal de dire object x = typeof(T); donc ne devrait pas object x = int; être légal dans votre proposition? Supposons qu'un type C a défini par l'utilisateur, la conversion implicite de Type de C; ne doit pas C c = string; être légal?

Mais laissons cela de côté pour un moment et considérer le bien-fondé de votre proposition. Par exemple, ce que vous proposez-vous de faire à ce sujet?

class C {
  public static string FullName = "Hello";
}
...
Type c = C;
Console.WriteLine(c.FullName); // "C"
Console.WriteLine(C.FullName); // "Hello"

N'est-ce pas vous frapper aussi bizarre que c == C mais c.FullName != C.FullName ? Un principe de base du langage de programmation conception est que vous pouvez farcir une expression dans une variable et la valeur de la variable se comporte comme l'expression, mais ce n'est pas le cas ici.

Votre proposition est fondamentalement que chaque expression qui désigne un type a un comportement différent selon qu'il est utilisé ou remis, et c'est super déroutant.

Maintenant, vous pourriez dire, eh bien, nous allons faire une syntaxe spéciale pour lever l'ambiguïté des situations où le type est utilisé dans les cas où le genre est mentionné, et il ya une telle syntaxe. C'est - typeof(T)! Si nous voulons traiter T.FullName comme T être Type nous disons typeof(T).FullName et si nous voulons traiter T comme étant un qualificatif dans une recherche que nous disons T.FullName, et maintenant nous avons propreté, de l'ambiguïté de ces cas, sans avoir à faire tout bidirectionnel d'inférence.

Fondamentalement, le problème fondamental est que les types ne sont pas de première classe en C#. Il y a des choses que vous pouvez faire avec les types que vous pouvez seulement le faire au moment de la compilation. Il n'y a pas:

Type t = b ? C : D;
List<t> l = new List<t>();

l est soit List<C> ou List<D> selon la valeur de b. Depuis les types sont très expressions spéciales, et en particulier sont des expressions qui n'ont aucune valeur au moment de l'exécution , ils ont besoin d'une syntaxe particulière qui appelle quand ils sont utilisés en tant que valeur.

Enfin, il est aussi un argument pour être faites quant à l'exactitude. Si un développeur écrit Foo(Bar.Blah) et Bar.Blah est un type, les chances sont très bons, ils ont fait une erreur et a pensé qu' Bar.Blah était une expression qui renvoie une valeur. Les cotes ne sont pas bonnes qu'ils ont l'intention de passer un Type de Foo.


Question de suivi:

pourquoi est-il possible avec la méthode des groupes lorsqu'ils sont passés à un délégué argument? Est-ce à cause de l'utilisation et de mentionner d'une méthode sont plus faciles à distinguer?

La méthode de groupes n'ont pas de membres; vous ne dites jamais:

class C { public void M() {} }
...
var x = C.M.Whatever;

parce qu' C.M n'ont pas de membres de tous. Donc, ce problème disparaît. Nous ne disons jamais "eh bien, C.M convertible en Action et Action a une méthode Invoke alors laissons C.M.Invoke(). Qui n'a tout simplement pas se produire. Encore une fois, la méthode des groupes ne sont pas de première classe, des valeurs. Seulement après, ils sont convertis aux délégués deviennent-elles des premières valeurs de classe.

Fondamentalement, la méthode des groupes sont traités comme des expressions qui ont une valeur , mais pas de type, puis la convertibilité des règles de déterminer quelle méthode les groupes sont convertibles à ce que délégué des types.

Maintenant, si vous allez faire de l'argument qu'une méthode de groupe doit être convertie implicitement MethodInfo et utilisées dans un contexte où une MethodInfo a été prévu, alors nous aurions à examiner le bien-fondé de ce. Il y a eu une proposition pour les décennies à faire une infoof opérateur (prononcé "en foof" bien sûr!) qui retourne un MethodInfo lorsque la méthode de groupe et un PropertyInfo lors d'une propriété et ainsi de suite, et que cette proposition a toujours échoué, car trop de travail de conception pour trop peu d'avantages. nameof était le prix bon marché à mettre en œuvre la version qui a été fait.


Une question que vous ne demandez pas, mais qui semble pertinente:

Vous avez dit qu' C.FullName pourrait être ambigu, car il serait difficile de savoir si C est Type ou le type C. Existe-il d'autres semblables à des ambiguïtés dans C#?

Oui! Considérer:

enum Color { Red }
class C {
  public Color Color { get; private set; }
  public void M(Color c) { }
  public void N(String s) { }
  public void O() {
    M(Color.Red);
    N(Color.ToString());
  }
}

Dans ce scénario, habilement appelé la "Couleur" du Problème, le compilateur C# parvient à comprendre qu' Color dans l'appel d' M signifie que le type, et que, dans l'appel d' N, cela signifie this.Color. Faire une recherche dans la spécification sur la "Couleur" et vous trouverez la règle, ou de voir le post de blog de la Couleur de la Couleur.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X