Attribut mit dem Konstruktor params object [] verursacht inkonsistente Compilerfehler

8

Ich erhalte den Fehler

  

Ein Attributargument muss ein konstanter Ausdruck, Ausdruckstyp oder Arrayerstellungsausdruck eines Attributparametertyps

sein

Beachten Sie den folgenden Screenshot:

Beachten Sie, dass bei der Verwendung des DataRow-Attributs mit einem oder drei Parametern kein Kompilierungsfehler auftritt. Aber wenn ich zwei Parameter verwende und der zweite Parameter ein Array von Strings ist, dann bekomme ich einen Kompilierfehler.

Die Signaturen für DataRowAttribute sind public DataRowAttribute (object data1); und public DataRowAttribute (object data1, params object[] moreData);

Der erste gibt mir kein Problem, aber der zweite scheint verwirrt zu werden.

Ich dachte, dass das params object[] vielleicht etwas Verwirrung verursachen könnte. Vielleicht konnte es nicht feststellen, ob ich [DataRow(new[] { 1 }, new[] { "1" })] oder [DataRow(new[] { 1 }, "1")]

meinte

Um das zu beheben, habe ich versucht, das zweite Attribut auf object ( [DataRow(new[] { 1 }, (object)new[] { "1" })] ) zu setzen, aber der Fehler ist nicht verschwunden und er hat mich gewarnt, dass der Cast redundant ist. Ich habe auch versucht, die Typen des Arrays explizit anzugeben, aber das hat auch nicht geholfen.

Ich könnte einfach einen dritten Dummy-Parameter hinzufügen, sogar null scheint das zu beheben, aber das ist nur ein Workaround. Was ist der richtige Weg, dies zu tun?

    
Dan Friedman 13.02.2018, 23:54
quelle

2 Antworten

11

tldr:

Die richtige Problemumgehung besteht darin, dem Compiler mitzuteilen, das erweiterte Formular nicht zu verwenden:

%Vor%

Übermäßige Analyse:

Die Antwort von Michael Randall ist grundsätzlich richtig. Lassen Sie uns nachsehen, indem Sie Ihr Beispiel vereinfachen:

%Vor%

Betrachten wir zuerst die Fälle, in denen keine Fehler aufgetreten sind.

%Vor%

Es gibt nicht genügend Argumente für die normale Form. Der Konstruktor ist in seiner erweiterten Form anwendbar. Der Compiler kompiliert dies, als ob Sie geschrieben hätten:

%Vor%

Als nächstes, was ist mit

? %Vor%

? Jetzt müssen wir entscheiden, ob der Konstruktor in seiner normalen oder erweiterten Form anwendbar ist. Es ist nicht in der normalen Form anwendbar, weil int[] nicht in object[] umwandelbar ist. Es ist in erweiterter Form anwendbar, also wird dies so kompiliert, als hättest du

geschrieben %Vor%

Nun, was ist mit

? %Vor%

Der Konstruktor ist sowohl in seiner normalen als auch in seiner expandierten Form anwendbar. Unter diesen Umständen gewinnt die normale Form. Der Compiler generiert den Aufruf wie beschrieben. Es umschließt das Objekt-Array NICHT in einem zweiten Objekt-Array.

Was ist mit

? %Vor%

? Es gibt zu viele Argumente für die normale Form. Das erweiterte Formular wird verwendet:

%Vor%

Das sollte alles unkompliziert sein. Was dann falsch ist mit:

%Vor%

? Nun, zuerst, ist es in normaler oder erweiterter Form anwendbar? Offensichtlich ist es in erweiterter Form anwendbar. Was nicht so offensichtlich ist, ist, dass es auch in normaler Form anwendbar ist. int[] konvertiert nicht implizit in object[] , aber string[] macht das! Dies ist eine unsichere kovariante Array-Referenzkonvertierung , und sie steht ganz oben auf meiner Liste für das "schlechteste C # -Feature".

Da die Überladungsauflösung angibt, dass dies sowohl in normaler als auch in erweiterter Form anwendbar ist, gewinnt die normale Form, und dies wird so kompiliert, als ob Sie

geschrieben hätten %Vor%

Lasst uns das erkunden. Wenn wir einige unserer Arbeitsfälle oben ändern:

%Vor%

Alle diese Elemente schlagen jetzt in früheren Versionen von C # fehl und sind in der aktuellen Version erfolgreich.

Offenbar hat der Compiler zuvor die no Konvertierung, nicht einmal eine Identitätskonvertierung, für das Objekt-Array zugelassen. Jetzt erlaubt es Identitätskonvertierungen, aber nicht kovariante Array-Konvertierungen.

Übertragungen, die mit der Zeitkonstantenwertanalyse analysiert werden können, sind zulässig. Sie können

tun %Vor%

wenn Sie möchten, denn diese Konvertierung wird vom konstanten Analysator entfernt. Aber der Attributanalysator hat keine Ahnung, was mit einer unerwarteten Umwandlung in object[] zu tun ist, daher gibt es einen Fehler.

Was ist mit dem anderen Fall, den Sie erwähnen? Das ist das Interessante!

%Vor%

Lassen Sie uns das nochmal durchdenken. Das ist nur in seiner erweiterten Form anwendbar, also sollte das so kompiliert werden, als hättest du

geschrieben %Vor%

Aber das ist legal . Um konsistent zu sein, sollten entweder beide Formen legal sein, oder beide sollten illegal sein - offen gesagt, es interessiert mich nicht so oder so - aber es ist bizarr, dass man legal ist und der andere nicht. Überlegen Sie, einen Fehler zu melden. (Wenn das tatsächlich ein Fehler ist, ist es wahrscheinlich meine Schuld. Tut mir leid.)

Das lange und kurze ist: das Mischen von params object [] mit Array-Argumenten ist ein Rezept für Verwirrung . Versuche es zu vermeiden. Wenn Sie sich in einer Situation befinden, in der Sie Arrays an eine Methode params object [] übergeben, rufen Sie sie in ihrer normalen Form auf. Machen Sie ein new object[] { ... } und legen Sie die Argumente selbst in das Array.

    
Eric Lippert 23.02.2018, 15:35
quelle
5

Angenommen, Ihr Konstruktor ist

%Vor%

Dann denke ich, dass Sie gegen einen übersehenen und nicht offensichtlichen Compiler Dark Magic ankämpfen.

Zum Beispiel wird offensichtlich das Folgende funktionieren

%Vor%

Das funktioniert auch für mich

%Vor%

Dies ist jedoch nicht

%Vor%
  

Ein Attributargument muss ein konstanter Ausdruck typeof sein   oder Array-Erstellungsausdruck eines Attributparametertyps

Ich denke, der Schlüssel zum Mitnehmen von Informationen hier ist

  

Eine Methode mit einem params-Array kann entweder als "normal" oder als "normal" bezeichnet werden   "erweiterte Form. Die normale Form ist so, als gäbe es keine "Params". Erweitert   form nimmt die params und bündelt sie zu einem Array das ist   automatisch erzeugt. Wenn beide Formen anwendbar sind, dann normale Form   gewinnt über erweitertes Formular.

Als Beispiel

%Vor%

Wenn ein Aufruf angegeben wird, der in beiden Formularen anwendbar ist, wählt der Compiler immer die normale Form über das erweiterte Formular.

Aber ich denke, das wird mit object[] und sogar Attributen noch unordentlicher.

Ich werde nicht so tun, als wüsste ich genau, was die CLR macht (und es gibt viel mehr qualifizierte Leute, die vielleicht antworten). Als Referenz sehen Sie sich jedoch die CLR SO Wizard Eric Lipperts ähnliche Antworten für eine detailliertere Erläuterung dessen an, was vor sich gehen könnte

C # params object [] seltsames Verhalten

Warum verhalten sich Params so?

Gibt es eine Möglichkeit, myFunc (1, 2, 3) von myFunc (new int [] {1, 2, 3}) zu trennen?

    
TheGeneral 14.02.2018 00:46
quelle

Tags und Links