Laden...

Bug in CodeDom GenerateEvent: MemberAttributes.New wird nicht berücksichtigt

Erstellt von Programmierhans vor 11 Jahren Letzter Beitrag vor 11 Jahren 1.624 Views
Programmierhans Themenstarter:in
4.221 Beiträge seit 2005
vor 11 Jahren
Bug in CodeDom GenerateEvent: MemberAttributes.New wird nicht berücksichtigt

Hallo Miteinander

Ich habe einen Bug gefunden im CodeDom. Kann das bitte jemand verifizieren.

Auf dem CodeDom generiere ich aufgrund von Metadaten gewisse Klassen. Dabei kann es vorkommen, dass eine Klasse vererbt wird. Die vererbte Klasse erbt natürlich auch die Events der BasisKlasse. Allerdings kann es sein, dass die vererbende Klasse denselben Event erweitern muss (es wird dabei ein EventArgs verwendet welches vom EventArgs der BasisKlasse abgeleitet ist).

Idealerweise blendet der Event der neuen Klasse den Event der BasisKlasse nur aus.

Folgendes Sample soll aufzeigen was ich mache.

Klasse A ist die Basisklasse mit einem Event vom Type EventHandler (EventArgs).

Klasse B leitet von A ab und will den Event ausblenden und stellt dafür einen Event (mit demselben Namen) vom Type CancelEventHandler bereit (dessen CancelEventArgs leitet ja von EventArgs ab).

Effektiv sind es natürlich eigene EventArgs-Klassen (aber das würde das Sample nur unnötig kompliziert machen).



using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.CodeDom;
using System.CodeDom.Compiler;

namespace BugInCodeComGenerateEvent
{
    class Program
    {
        static void Main(string[] args)
        {
            string strGeneratedCode = null;
            System.CodeDom.CodeCompileUnit ccu = new System.CodeDom.CodeCompileUnit();
            System.CodeDom.Compiler.CodeDomProvider provider = System.CodeDom.Compiler.CodeDomProvider.CreateProvider("CSharp");

            CodeNamespace ns = new CodeNamespace("Bug");
            ccu.Namespaces.Add(ns);

            //Create a new Class  A
            CodeTypeDeclaration ctdA = new CodeTypeDeclaration("A");
            ns.Types.Add(ctdA);
            
            //Create a new Event TestEvent in A
            CodeMemberEvent cmeATestEvent = new CodeMemberEvent();
            cmeATestEvent.Name = "TestEvent";
            cmeATestEvent.Type = new CodeTypeReference(typeof(EventHandler));
            cmeATestEvent.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            ctdA.Members.Add(cmeATestEvent);

            //Create a new Class B (Inherits from A)
            CodeTypeDeclaration ctdB = new CodeTypeDeclaration("B");
            ctdB.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            ctdB.BaseTypes.Add("A");
            ns.Types.Add(ctdB);

            //Create a new Event TestEvent in B
            CodeMemberEvent cmeBTestEvent = new CodeMemberEvent();
            cmeBTestEvent.Name = "TestEvent";
            cmeBTestEvent.Type = new CodeTypeReference(typeof(System.ComponentModel.CancelEventHandler));
            cmeBTestEvent.Attributes = MemberAttributes.Public | MemberAttributes.Final | MemberAttributes.New;
            cmeBTestEvent.Comments.Add(new CodeCommentStatement("public event EventHandler TestEvent; is wrong (new-Attribute is missing)"));
            cmeBTestEvent.Comments.Add(new CodeCommentStatement("public new event EventHandler TestEvent; would be correct"));
            ctdB.Members.Add(cmeBTestEvent);

            /*
             * Have a look at: Class: Microsoft.CSharp.CSharpCodeGenerator
             * 
             * Compare Methods: GenerateMethod with GenerateEvent
             * 
             * GenerateMethod calls:
             * 
             * 			this.OutputMemberAccessModifier(e.Attributes);    (public / internal protected ....)
			            this.OutputVTableModifier(e.Attributes);          (new )
			            this.OutputMemberScopeModifier(e.Attributes);     (abstract / virtual ....)
             * 
             * GenerateEvent only calls:
             * 
             * this.OutputMemberAccessModifier(e.Attributes);    (public / internal protected ....)
             * 
             * --- Calls to OutputVTableModifier and OutputMemberScopeModifier are missing
             */

            using (MemoryStream ms = new MemoryStream())
            {
                IndentedTextWriter tw = new IndentedTextWriter(new StreamWriter(ms));
                // Generate source code using the code generator.
                provider.GenerateCodeFromCompileUnit(ccu, tw, new CodeGeneratorOptions());
                // Close the output file.
                tw.Close();
                strGeneratedCode = System.Text.Encoding.UTF8.GetString(ms.GetBuffer());
            }
            System.Diagnostics.Debug.WriteLine(strGeneratedCode);
            Console.WriteLine(strGeneratedCode);
        }
    }
}



myCSharp hat doch gute Kontakte zu MS... falls sich der Fehler bestätigt... wäre doch toll wenn das Forum den Fehler zu MS rüberschieben könnte.

Edit: Hier noch der Output:


namespace Bug {
    
    
    public class A {
        
        public event System.EventHandler TestEvent;
    }
    
    public class B : A {
        
        // public event EventHandler TestEvent; is wrong (new-Attribute is missing)
        // public new event EventHandler TestEvent; would be correct
        public event System.ComponentModel.CancelEventHandler TestEvent;
    }
}

Gruss
Programmierhans

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo Programmierhans,

im Grunde ist doch die Frage, warum wird der Event in B trotz der Zeile

cmeBTestEvent.Attributes = MemberAttributes.Public | MemberAttributes.Final | MemberAttributes.New;  

nicht als new markiert. Auch dann nicht, wenn man an allen Stellen im Code das MemberAttributes.Final weglässt. Wenn man dagegen testweise CodeMemberEvent durch CodeMemberField ersetzt, wird das new eingefügt. Das spricht schon dafür, dass es sich um einen Bug handelt.

Ich persönlich habe keine Kontakte zu Microsoft. Aber ich denke, du könnten den Fehler auch selber auf http://connect.microsoft.com melden.

Anderseits produziert ein fehlendes new nur eine Warnung:

Fehlermeldung:
warning CS0108: "B.TestEvent" blendet den vererbten Member "A.TestEvent" aus. Verwenden Sie das new-Schlüsselwort, wenn das Ausblenden vorgesehen war.

Wenn man die Warnung ignoriert, funktioniert alles wie gewünscht.

herbivore

5.657 Beiträge seit 2006
vor 11 Jahren

Hi Programmierhans,

ich seh das genauso wie herbivore. Ich verstehe zwar auch nicht, warum du die Events als sealed deklariert hast, aber auch ich hab keinen Weg gefunden, ein new event zu erzeugen. Auch nicht mit statischen Events, während beides für Felder und Methoden funktioniert.

Christian

Weeks of programming can save you hours of planning

Programmierhans Themenstarter:in
4.221 Beiträge seit 2005
vor 11 Jahren

Danke euch beiden...

Gibt es allenfalls ein Attribut [CompilerHaltFresseBeiWarnung(CSxx)] welches ich noch drauf generieren könnte um den Compiler zum Schweigen zu bringen (ich habe keines gefunden)...

Sonst werde ich mit der Warnung leben ... gibt schlimmere Probleme.

Gruss
Programmierhans

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo Programmierhans,

es gibt #pragma warning (C#-Referenz). Das scheint man auch per CodeDom generieren zu können, siehe z.B. How to add #pragma warning disable 1591 using CodeDom.

herbivore

Programmierhans Themenstarter:in
4.221 Beiträge seit 2005
vor 11 Jahren

Hallo herbivore

Nicht schlecht die Idee... wobei bei einem Event kann ich das Pragma nur als Member des Typen einfügen (es kommt dann einfach immer ganz am Anfang der Klasse)...


ctdB.Members.Add(new CodeSnippetTypeMember("#pragma warning disable 0108"));

Aber so ist die Warnung nun weg.

Danke sehr vielmals

Gruss
Programmierhans

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...