The Labs \ Source Viewer \ SSCLI \ System.ComponentModel \ DebugReflectEventDescriptor

  1. //------------------------------------------------------------------------------
  2. // <copyright file="DebugReflectEventDescriptor.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. #if DEBUG
  16. /*
  17.    
  18.     This class exists in debug only.  It is a complete copy of the
  19.     V1.0 TypeDescriptor object and is used to validate that the
  20.     behavior of the V2.0 TypeDescriptor matches 1.0 behavior.
  21.    
  22. */
  23. namespace System.ComponentModel
  24. {
  25.    
  26.    
  27.     using System.Diagnostics;
  28.    
  29.     using System;
  30.     using System.Reflection;
  31.     using System.Collections;
  32.     using Microsoft.Win32;
  33.     using System.ComponentModel.Design;
  34.     using System.Security.Permissions;
  35.    
  36.     /// <internalonly/>
  37.     /// <devdoc>
  38.     /// <para>
  39.     /// DebugReflectEventDescriptor defines an event. Events are the main way that a user can get
  40.     /// run-time notifications from a component.
  41.     /// The DebugReflectEventDescriptor class takes a component class that the event lives on,
  42.     /// the event name, the type of the event handling delegate, and various
  43.     /// attributes for the event.
  44.     /// Every event has a structure through which it passes it's information. The base
  45.     /// structure, Event, is empty, and there is a default instance, Event.EMPTY, which
  46.     /// is usually passed. When addOnXXX is invoked, it needs a pointer to a method
  47.     /// that takes a source object (the object that fired the event) and a structure
  48.     /// particular to that event. It also needs a pointer to the instance of the
  49.     /// object the method should be invoked on. These two things are what composes a
  50.     /// delegate. An event handler is
  51.     /// a delegate, and the compiler recognizes a special delegate syntax that makes
  52.     /// using delegates easy.
  53.     /// For example, to listen to the click event on a button in class Foo, the
  54.     /// following code will suffice:
  55.     /// </para>
  56.     /// <code>
  57.     /// class Foo {
  58.     /// Button button1 = new Button();
  59.     /// void button1_click(Object sender, Event e) {
  60.     /// // do something on button1 click.
  61.     /// }
  62.     /// public Foo() {
  63.     /// button1.addOnClick(this.button1_click);
  64.     /// }
  65.     /// }
  66.     /// </code>
  67.     /// For an event named XXX, a YYYEvent structure, and a YYYEventHandler delegate,
  68.     /// a component writer is required to implement two methods of the following
  69.     /// form:
  70.     /// <code>
  71.     /// public void addOnXXX(YYYEventHandler handler);
  72.     /// public void removeOnXXX(YYYEventHandler handler);
  73.     /// </code>
  74.     /// YYYEventHandler should be an event handler declared as
  75.     /// <code>
  76.     /// public multicast delegate void YYYEventHandler(Object sender, YYYEvent e);
  77.     /// </code>
  78.     /// Note that this event was declared as a multicast delegate. This allows multiple
  79.     /// listeners on an event. This is not a requirement.
  80.     /// Various attributes can be passed to the DebugReflectEventDescriptor, as are described in
  81.     /// Attribute.
  82.     /// ReflectEventDescriptors can be obtained by a user programmatically through the
  83.     /// ComponentManager.
  84.     /// </devdoc>
  85.     [HostProtection(SharedState = true)]
  86.     internal sealed class DebugReflectEventDescriptor : EventDescriptor
  87.     {
  88.        
  89.         private static readonly Type[] argsNone = new Type[0];
  90.         private static readonly object noDefault = new object();
  91.        
  92.         private Type type;
  93.         // the delegate type for the event
  94.         private readonly Type componentClass;
  95.         // the class of the component this info is for
  96.         internal MethodInfo addMethod;
  97.         // the method to use when adding an event
  98.         internal MethodInfo removeMethod;
  99.         // the method to use when removing an event
  100.         private EventInfo realEvent;
  101.         // actual event info... may be null
  102.         private bool filledMethods = false;
  103.         // did we already call FillMethods() once?
  104.         /// <devdoc>
  105.         /// This is the main constructor for an DebugReflectEventDescriptor.
  106.         /// </devdoc>
  107.         public DebugReflectEventDescriptor(Type componentClass, string name, Type type, Attribute[] attributes) : base(name, attributes)
  108.         {
  109.             if (componentClass == null) {
  110.                 throw new ArgumentException(SR.GetString(SR.InvalidNullArgument, "componentClass"));
  111.             }
  112.             if (type == null || !(typeof(Delegate)).IsAssignableFrom(type)) {
  113.                 throw new ArgumentException(SR.GetString(SR.ErrorInvalidEventType, name));
  114.             }
  115.             Debug.Assert(type.IsSubclassOf(typeof(Delegate)), "Not a valid ReflectEvent: " + componentClass.FullName + "." + name + " " + type.FullName);
  116.             this.componentClass = componentClass;
  117.             this.type = type;
  118.         }
  119.        
  120.         public DebugReflectEventDescriptor(Type componentClass, EventInfo eventInfo) : base(eventInfo.Name, new Attribute[0])
  121.         {
  122.            
  123.             if (componentClass == null) {
  124.                 throw new ArgumentException(SR.GetString(SR.InvalidNullArgument, "componentClass"));
  125.             }
  126.             this.componentClass = componentClass;
  127.             this.realEvent = eventInfo;
  128.         }
  129.        
  130.         /// <devdoc>
  131.         /// This constructor takes an existing DebugReflectEventDescriptor and modifies it by merging in the
  132.         /// passed-in attributes.
  133.         /// </devdoc>
  134.         public DebugReflectEventDescriptor(Type componentType, EventDescriptor oldReflectEventDescriptor, Attribute[] attributes) : base(oldReflectEventDescriptor, attributes)
  135.         {
  136.             this.componentClass = componentType;
  137.             this.type = oldReflectEventDescriptor.EventType;
  138.            
  139.             if (oldReflectEventDescriptor is DebugReflectEventDescriptor) {
  140.                 this.addMethod = ((DebugReflectEventDescriptor)oldReflectEventDescriptor).addMethod;
  141.                 this.removeMethod = ((DebugReflectEventDescriptor)oldReflectEventDescriptor).removeMethod;
  142.                 this.filledMethods = true;
  143.             }
  144.         }
  145.        
  146.         /// <devdoc>
  147.         /// Retrieves the type of the component this EventDescriptor is bound to.
  148.         /// </devdoc>
  149.         public override Type ComponentType {
  150.             get { return componentClass; }
  151.         }
  152.        
  153.         /// <devdoc>
  154.         /// Retrieves the type of the delegate for this event.
  155.         /// </devdoc>
  156.         public override Type EventType {
  157.             get {
  158.                 FillMethods();
  159.                 return type;
  160.             }
  161.         }
  162.        
  163.         /// <devdoc>
  164.         /// Indicates whether the delegate type for this event is a multicast
  165.         /// delegate.
  166.         /// </devdoc>
  167.         public override bool IsMulticast {
  168.             get { return (typeof(MulticastDelegate)).IsAssignableFrom(EventType); }
  169.         }
  170.        
  171.         /// <devdoc>
  172.         /// This adds the delegate value as a listener to when this event is fired
  173.         /// by the component, invoking the addOnXXX method.
  174.         /// </devdoc>
  175.         public override void AddEventHandler(object component, Delegate value)
  176.         {
  177.             FillMethods();
  178.            
  179.             if (component != null) {
  180.                 ISite site = GetSite(component);
  181.                 IComponentChangeService changeService = null;
  182.                
  183.                 // Announce that we are about to change this component
  184.                 //
  185.                 if (site != null) {
  186.                     changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService));
  187.                     Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || changeService != null, "IComponentChangeService not found");
  188.                 }
  189.                
  190.                 if (changeService != null) {
  191.                     try {
  192.                         changeService.OnComponentChanging(component, this);
  193.                     }
  194.                     catch (CheckoutException coEx) {
  195.                         if (coEx == CheckoutException.Canceled) {
  196.                             return;
  197.                         }
  198.                         throw coEx;
  199.                     }
  200.                 }
  201.                
  202.                 bool shadowed = false;
  203.                
  204.                 if (site != null && site.DesignMode) {
  205.                     // Events are final, so just check the class
  206.                     if (EventType != value.GetType()) {
  207.                         throw new ArgumentException(SR.GetString(SR.ErrorInvalidEventHandler, Name));
  208.                     }
  209.                     IDictionaryService dict = (IDictionaryService)site.GetService(typeof(IDictionaryService));
  210.                     Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || dict != null, "IDictionaryService not found");
  211.                     if (dict != null) {
  212.                         Delegate eventdesc = (Delegate)dict.GetValue(this);
  213.                         eventdesc = Delegate.Combine(eventdesc, value);
  214.                         dict.SetValue(this, eventdesc);
  215.                         shadowed = true;
  216.                     }
  217.                 }
  218.                
  219.                 if (!shadowed) {
  220.                     addMethod.Invoke(component, new object[] {value});
  221.                 }
  222.                
  223.                 // Now notify the change service that the change was successful.
  224.                 //
  225.                 if (changeService != null) {
  226.                     changeService.OnComponentChanged(component, this, null, value);
  227.                 }
  228.             }
  229.         }
  230.        
  231.         // <doc>
  232.         // <desc>
  233.         // Adds in custom attributes found on either the AddOn or RemoveOn method...
  234.         // </desc>
  235.         // </doc>
  236.         //
  237.         protected override void FillAttributes(IList attributes)
  238.         {
  239.            
  240.             //
  241.             // The order that we fill in attributes is critical. The list of attributes will be
  242.             // filtered so that matching attributes at the end of the list replace earlier matches
  243.             // (last one in wins). Therefore, the two categories of attributes we add must be
  244.             // added as follows:
  245.             //
  246.             // 1. Attributes of the event, from base class to most derived. This way
  247.             // derived class attributes replace base class attributes.
  248.             //
  249.             // 2. Attributes from our base MemberDescriptor. While this seems opposite of what
  250.             // we want, MemberDescriptor only has attributes if someone passed in a new
  251.             // set in the constructor. Therefore, these attributes always
  252.             // supercede existing values.
  253.             //
  254.            
  255.             FillMethods();
  256.             Debug.Assert(componentClass != null, "Must have a component class for FilterAttributes");
  257.             if (realEvent != null) {
  258.                 FillEventInfoAttribute(realEvent, attributes);
  259.             }
  260.             else {
  261.                 Debug.Assert(removeMethod != null, "Null remove method for " + Name);
  262.                 FillSingleMethodAttribute(removeMethod, attributes);
  263.                
  264.                 Debug.Assert(addMethod != null, "Null remove method for " + Name);
  265.                 FillSingleMethodAttribute(addMethod, attributes);
  266.             }
  267.            
  268.             // Include the base attributes. These override all attributes on the actual
  269.             // property, so we want to add them last.
  270.             //
  271.             base.FillAttributes(attributes);
  272.         }
  273.        
  274.         private void FillEventInfoAttribute(EventInfo realEventInfo, IList attributes)
  275.         {
  276.             string eventName = realEventInfo.Name;
  277.             BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly;
  278.             Type currentReflectType = realEventInfo.ReflectedType;
  279.             Debug.Assert(currentReflectType != null, "currentReflectType cannot be null");
  280.             int depth = 0;
  281.            
  282.             // First, calculate the depth of the object hierarchy. We do this so we can do a single
  283.             // object create for an array of attributes.
  284.             //
  285.             while (currentReflectType != typeof(object)) {
  286.                 depth++;
  287.                 currentReflectType = currentReflectType.BaseType;
  288.             }
  289.            
  290.             if (depth > 0) {
  291.                 // Now build up an array in reverse order
  292.                 //
  293.                 currentReflectType = realEventInfo.ReflectedType;
  294.                 object[][] attributeStack = new object[depth][];
  295.                
  296.                 while (currentReflectType != typeof(object)) {
  297.                    
  298.                     // Fill in our member info so we can get at the custom attributes.
  299.                     //
  300.                     MemberInfo memberInfo = currentReflectType.GetEvent(eventName, bindingFlags);
  301.                    
  302.                     // Get custom attributes for the member info.
  303.                     //
  304.                     if (memberInfo != null) {
  305.                         attributeStack[--depth] = DebugTypeDescriptor.GetCustomAttributes(memberInfo);
  306.                     }
  307.                    
  308.                     // Ready for the next loop iteration.
  309.                     //
  310.                     currentReflectType = currentReflectType.BaseType;
  311.                 }
  312.                
  313.                 // Now trawl the attribute stack so that we add attributes
  314.                 // from base class to most derived.
  315.                 //
  316.                 foreach (object[] attributeArray in attributeStack) {
  317.                     if (attributeArray != null) {
  318.                         foreach (object attr in attributeArray) {
  319.                             if (attr is Attribute) {
  320.                                 attributes.Add(attr);
  321.                             }
  322.                         }
  323.                     }
  324.                 }
  325.             }
  326.         }
  327.        
  328.         /// <devdoc>
  329.         /// This fills the get and set method fields of the event info. It is shared
  330.         /// by the various constructors.
  331.         /// </devdoc>
  332.         private void FillMethods()
  333.         {
  334.             if (filledMethods)
  335.                 return;
  336.            
  337.             if (realEvent != null) {
  338.                 addMethod = realEvent.GetAddMethod();
  339.                 removeMethod = realEvent.GetRemoveMethod();
  340.                
  341.                 EventInfo defined = null;
  342.                
  343.                 if (addMethod == null || removeMethod == null) {
  344.                     Type start = componentClass.BaseType;
  345.                     while (start != null && start != typeof(object)) {
  346.                         BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
  347.                         EventInfo test = start.GetEvent(realEvent.Name, bindingFlags);
  348.                         if (test.GetAddMethod() != null) {
  349.                             defined = test;
  350.                             break;
  351.                         }
  352.                     }
  353.                 }
  354.                
  355.                 if (defined != null) {
  356.                     addMethod = defined.GetAddMethod();
  357.                     removeMethod = defined.GetRemoveMethod();
  358.                     type = defined.EventHandlerType;
  359.                 }
  360.                 else {
  361.                     type = realEvent.EventHandlerType;
  362.                 }
  363.             }
  364.             else {
  365.                
  366.                 // first, try to get the eventInfo...
  367.                 //
  368.                 realEvent = this.componentClass.GetEvent(Name);
  369.                 if (realEvent != null) {
  370.                     // if we got one, just recurse and return.
  371.                     //
  372.                     FillMethods();
  373.                     return;
  374.                 }
  375.                
  376.                 Type[] argsType = new Type[] {type};
  377.                 addMethod = FindMethod(componentClass, "AddOn" + Name, argsType, typeof(void));
  378.                 removeMethod = FindMethod(componentClass, "RemoveOn" + Name, argsType, typeof(void));
  379.                 if (addMethod == null || removeMethod == null) {
  380.                     Debug.Fail("Missing event accessors for " + componentClass.FullName + "." + Name);
  381.                     throw new ArgumentException(SR.GetString(SR.ErrorMissingEventAccessors, Name));
  382.                 }
  383.             }
  384.            
  385.             filledMethods = true;
  386.         }
  387.        
  388.         private void FillSingleMethodAttribute(MethodInfo realMethodInfo, IList attributes)
  389.         {
  390.            
  391.             string methodName = realMethodInfo.Name;
  392.             BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly;
  393.             Type currentReflectType = realMethodInfo.ReflectedType;
  394.             Debug.Assert(currentReflectType != null, "currentReflectType cannot be null");
  395.            
  396.             // First, calculate the depth of the object hierarchy. We do this so we can do a single
  397.             // object create for an array of attributes.
  398.             //
  399.             int depth = 0;
  400.             while (currentReflectType != null && currentReflectType != typeof(object)) {
  401.                 depth++;
  402.                 currentReflectType = currentReflectType.BaseType;
  403.             }
  404.            
  405.             if (depth > 0) {
  406.                 // Now build up an array in reverse order
  407.                 //
  408.                 currentReflectType = realMethodInfo.ReflectedType;
  409.                 object[][] attributeStack = new object[depth][];
  410.                
  411.                 while (currentReflectType != null && currentReflectType != typeof(object)) {
  412.                     // Fill in our member info so we can get at the custom attributes.
  413.                     //
  414.                     MemberInfo memberInfo = currentReflectType.GetMethod(methodName, bindingFlags);
  415.                    
  416.                     // Get custom attributes for the member info.
  417.                     //
  418.                     if (memberInfo != null) {
  419.                         attributeStack[--depth] = DebugTypeDescriptor.GetCustomAttributes(memberInfo);
  420.                     }
  421.                    
  422.                     // Ready for the next loop iteration.
  423.                     //
  424.                     currentReflectType = currentReflectType.BaseType;
  425.                 }
  426.                
  427.                 // Now trawl the attribute stack so that we add attributes
  428.                 // from base class to most derived.
  429.                 //
  430.                 foreach (object[] attributeArray in attributeStack) {
  431.                     if (attributeArray != null) {
  432.                         foreach (object attr in attributeArray) {
  433.                             if (attr is Attribute) {
  434.                                 attributes.Add(attr);
  435.                             }
  436.                         }
  437.                     }
  438.                 }
  439.             }
  440.         }
  441.        
  442.         /// <devdoc>
  443.         /// This will remove the delegate value from the event chain so that
  444.         /// it no longer gets events from this component.
  445.         /// </devdoc>
  446.         public override void RemoveEventHandler(object component, Delegate value)
  447.         {
  448.             FillMethods();
  449.            
  450.             if (component != null) {
  451.                 ISite site = GetSite(component);
  452.                 IComponentChangeService changeService = null;
  453.                
  454.                 // Announce that we are about to change this component
  455.                 //
  456.                 if (site != null) {
  457.                     changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService));
  458.                     Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || changeService != null, "IComponentChangeService not found");
  459.                 }
  460.                
  461.                 if (changeService != null) {
  462.                     try {
  463.                         changeService.OnComponentChanging(component, this);
  464.                     }
  465.                     catch (CheckoutException coEx) {
  466.                         if (coEx == CheckoutException.Canceled) {
  467.                             return;
  468.                         }
  469.                         throw coEx;
  470.                     }
  471.                 }
  472.                
  473.                 bool shadowed = false;
  474.                
  475.                 if (site != null && site.DesignMode) {
  476.                     IDictionaryService dict = (IDictionaryService)site.GetService(typeof(IDictionaryService));
  477.                     Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || dict != null, "IDictionaryService not found");
  478.                     if (dict != null) {
  479.                         Delegate del = (Delegate)dict.GetValue(this);
  480.                         del = Delegate.Remove(del, value);
  481.                         dict.SetValue(this, del);
  482.                         shadowed = true;
  483.                     }
  484.                 }
  485.                
  486.                 if (!shadowed) {
  487.                     removeMethod.Invoke(component, new object[] {value});
  488.                 }
  489.                
  490.                 // Now notify the change service that the change was successful.
  491.                 //
  492.                 if (changeService != null) {
  493.                     changeService.OnComponentChanged(component, this, null, value);
  494.                 }
  495.             }
  496.         }
  497.        
  498.        
  499.         /*
  500.             The following code has been removed to fix FXCOP violations.  The code
  501.             is left here incase it needs to be resurrected in the future.
  502.         /// <devdoc>
  503.         ///    This is a shortcut main constructor for an DebugReflectEventDescriptor with one attribute.
  504.         /// </devdoc>
  505.         public DebugReflectEventDescriptor(Type componentClass, string name, Type type, MethodInfo addMethod, MethodInfo removeMethod) : this(componentClass, name, type, (Attribute[]) null) {
  506.             this.addMethod = addMethod;
  507.             this.removeMethod = removeMethod;
  508.             this.filledMethods = true;
  509.         }
  510.         /// <devdoc>
  511.         ///    This is a shortcut main constructor for an DebugReflectEventDescriptor with one attribute.
  512.         /// </devdoc>
  513.         public DebugReflectEventDescriptor(Type componentClass, string name, Type type) : this(componentClass, name, type, (Attribute[]) null) {
  514.         }
  515.         /// <devdoc>
  516.         ///    This is a shortcut main constructor for an DebugReflectEventDescriptor with two attributes.
  517.         /// </devdoc>
  518.         public DebugReflectEventDescriptor(Type componentClass, string name, Type type,
  519.                                       Attribute a1) : this(componentClass, name, type, new Attribute[] {a1}) {
  520.         }
  521.         /// <devdoc>
  522.         ///    This is a shortcut main constructor for an DebugReflectEventDescriptor with two attributes.
  523.         /// </devdoc>
  524.         public DebugReflectEventDescriptor(Type componentClass, string name, Type type,
  525.                                       Attribute a1, Attribute a2) : this(componentClass, name, type, new Attribute[] {a1, a2}) {
  526.         }
  527.         /// <devdoc>
  528.         ///    This is a shortcut main constructor for an DebugReflectEventDescriptor with three attributes.
  529.         /// </devdoc>
  530.         public DebugReflectEventDescriptor(Type componentClass, string name, Type type,
  531.                                       Attribute a1, Attribute a2, Attribute a3) : this(componentClass, name, type, new Attribute[] {a1, a2, a3}) {
  532.         }
  533.         /// <devdoc>
  534.         ///    This is a shortcut main constructor for an DebugReflectEventDescriptor with four attributes.
  535.         /// </devdoc>
  536.         public DebugReflectEventDescriptor(Type componentClass, string name, Type type,
  537.                                       Attribute a1, Attribute a2,
  538.                                       Attribute a3, Attribute a4) : this(componentClass, name, type, new Attribute[] {a1, a2, a3, a4}) {
  539.         }
  540.         /// <devdoc>
  541.         ///    This constructor takes an existing DebugReflectEventDescriptor and modifies it by merging in the
  542.         ///    passed-in attributes.
  543.         /// </devdoc>
  544.         public DebugReflectEventDescriptor(EventDescriptor oldReflectEventDescriptor, Attribute[] attributes)
  545.         : this(oldReflectEventDescriptor.ComponentType, oldReflectEventDescriptor, attributes) {
  546.         }
  547.         /// <devdoc>
  548.         ///    This is a shortcut constructor that takes an existing DebugReflectEventDescriptor and one attribute to
  549.         ///    merge in.
  550.         /// </devdoc>
  551.         public DebugReflectEventDescriptor(EventDescriptor oldReflectEventDescriptor, Attribute a1) : this(oldReflectEventDescriptor, new Attribute[] { a1}) {
  552.         }
  553.         /// <devdoc>
  554.         ///    This is a shortcut constructor that takes an existing DebugReflectEventDescriptor and two attributes to
  555.         ///    merge in.
  556.         /// </devdoc>
  557.         public DebugReflectEventDescriptor(EventDescriptor oldReflectEventDescriptor, Attribute a1,
  558.                                       Attribute a2) : this(oldReflectEventDescriptor, new Attribute[] { a1,a2}) {
  559.         }
  560.         /// <devdoc>
  561.         ///    This is a shortcut constructor that takes an existing DebugReflectEventDescriptor and three attributes to
  562.         ///    merge in.
  563.         /// </devdoc>
  564.         public DebugReflectEventDescriptor(EventDescriptor oldReflectEventDescriptor, Attribute a1,
  565.                                       Attribute a2, Attribute a3) : this(oldReflectEventDescriptor, new Attribute[] { a1,a2,a3}) {
  566.         }
  567.         /// <devdoc>
  568.         ///    This is a shortcut constructor that takes an existing DebugReflectEventDescriptor and four attributes to
  569.         ///    merge in.
  570.         /// </devdoc>
  571.         public DebugReflectEventDescriptor(EventDescriptor oldReflectEventDescriptor, Attribute a1,
  572.                                       Attribute a2, Attribute a3, Attribute a4) : this(oldReflectEventDescriptor, new Attribute[] { a1,a2,a3,a4}) {
  573.         }
  574.         */       
  575.        
  576.     }
  577. }
  578. #endif

Developer Fusion