Announcement

Collapse
No announcement yet.

Partner 728x90

Collapse

Attribute question

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Attribute question

    Is an attribute's property a reference to an attribute, or does each property get its own copy of the attribute? If it is a reference, how can I break that, ensuring that each property instance gets its own instance of the attribute, not a reference?

    I can believe that the attribute may be a reference, since an attribute is a class, which is a reference type. What is happening in my case is that I have Indicators A and B, both derived from a common BaseClass. There is a BaseClass property with, naturally, the [Display ...] attribute. Both A and B update Display at runtime with the proper name and description for their specific indicator. The problem is that the last one wins. Looking at it in the debugger (Visual Studio) when B gets to do its work, the attribute is already showing the value A set its attribute to (i.e. not showing the default value it should be showing). B goes ahead and sets its name and description -- and these show up for both B and A in the GUI.

    It sure looks to me as if the Display attribute is a reference. If so, I need to break that so each indicator has its own values. Any help will be appreciated.

    --EV

    P.S. the problem I am trying to solve is that the [Display] name and description for this base class property cannot be known at compile time, since they depend on the derived class. Hence the attempt to set them at run time. I just need to solve the problem -- handling a reference is academic if there is a better way to solve the problem. I'd still like to understand about the reference issue, but the #1 thing is just to solve the problem.
    Last edited by ETFVoyageur; 09-08-2015, 02:32 AM.

    #2
    I just put in a little more diagnostic debugging code. What I am seeing is that the PropertyDescriptor is different for the two indicators, but the Atttribute fetched from that descriptor is the same for both indicators. That looks like it confirms my worry that the attribute is a reference to an attribute, not a separate instance.

    Here is the code, in case I have an error. Note that much of the code is not permanent -- it is just there for diagnostic purposes. In particular, the XXX.Equals() code is what is telling me that the descriptor is different, but the attribute is the same.

    Right now I am sitting at a breakpoint on line "sAttr = da;". The second indicator has just called this code and:
    • bDesc = false, showing the property descriptors are not the same (good)
    • bAttr = true, showing the attribute is the same for both instances (bad)
    • The problem I see is that bAttr should also be false

    --EV

    Code:
            static PropertyDescriptorCollection sDesc;
            bool bDesc;
            static Attribute sAttr;
            bool bAttr;
            protected void setDisplayAttribute(string strPropertyName, string name, string description)
            {
                // Get the property's descriptor (GetType() gets the leaf node's type)
                var dp = TypeDescriptor.GetProperties(this.GetType());
                if (strPropertyName == "EnableSignalLine")
                {
                    if (sDesc != null)
                        bDesc = (sDesc.Equals(dp));
                    sDesc = dp;
                }
    
                PropertyDescriptor descriptor = TypeDescriptor.GetProperties(this.GetType())[strPropertyName];
    
                // Get the Descriptor's "Display" Attribute
                var da = descriptor.Attributes[typeof(DisplayAttribute)];
                if (strPropertyName == "EnableSignalLine")
                {
                    if (sAttr != null)
                        bAttr = (sAttr.Equals(da));
                    sAttr = da;
                }
    
                DisplayAttribute attribute = (DisplayAttribute)descriptor.Attributes[typeof(DisplayAttribute)];
    
                // Change the values
                attribute.Name = name;
                attribute.Description = description;
            }

    Comment


      #3
      Originally posted by ETFVoyageur View Post
      I just put in a little more diagnostic debugging code. What I am seeing is that the PropertyDescriptor is different for the two indicators, but the Atttribute fetched from that descriptor is the same for both indicators. That looks like it confirms my worry that the attribute is a reference to an attribute, not a separate instance.

      Here is the code, in case I have an error. Note that much of the code is not permanent -- it is just there for diagnostic purposes. In particular, the XXX.Equals() code is what is telling me that the descriptor is different, but the attribute is the same.

      Right now I am sitting at a breakpoint on line "sAttr = da;". The second indicator has just called this code and:
      • bDesc = false, showing the property descriptors are not the same (good)
      • bAttr = true, showing the attribute is the same for both instances (bad)
      • The problem I see is that bAttr should also be false

      --EV

      Code:
              static PropertyDescriptorCollection sDesc;
              bool bDesc;
              static Attribute sAttr;
              bool bAttr;
              protected void setDisplayAttribute(string strPropertyName, string name, string description)
              {
                  // Get the property's descriptor (GetType() gets the leaf node's type)
                  var dp = TypeDescriptor.GetProperties(this.GetType());
                  if (strPropertyName == "EnableSignalLine")
                  {
                      if (sDesc != null)
                          bDesc = (sDesc.Equals(dp));
                      sDesc = dp;
                  }
      
                  PropertyDescriptor descriptor = TypeDescriptor.GetProperties(this.GetType())[strPropertyName];
      
                  // Get the Descriptor's "Display" Attribute
                  var da = descriptor.Attributes[typeof(DisplayAttribute)];
                  if (strPropertyName == "EnableSignalLine")
                  {
                      if (sAttr != null)
                          bAttr = (sAttr.Equals(da));
                      sAttr = da;
                  }
      
                  DisplayAttribute attribute = (DisplayAttribute)descriptor.Attributes[typeof(DisplayAttribute)];
      
                  // Change the values
                  attribute.Name = name;
                  attribute.Description = description;
              }
      If you want to replace an attribute at runtime, you will have to use reflection. remember how we can dynamically hide and/or show an attribute based on the value of a different attribute, by manipulating the relevant Browsable attribute? Use that code and modify it to set an attribute as required. I seem to remember that you even posted a solution on this forum some time back?

      Comment


        #4
        On second thoughts, it would appear that my answer is not correct. We have already been down this road, as I said, and you have rediscovered what you found then. (About a year ago)

        ref: http://ninjatrader.com/support/forum...64&postcount=7

        Comment


          #5
          Thanks for the reference. I was hoping that things had gotten fixed in NT8. I guess not.

          It seems to me to be pretty darned sad that each indicator instance gets its own copy of the property itself, but any attributes of that property are shared. The traditional design is to share a value, but to fork (no longer share) when an instance writes to that value (i.e. copy-on-write). There needs to be some way to prevent an attribute being shared when that sharing is unwanted. It seems to me to be a pretty poor design where two independent instances are forced to have a shared value.

          Support folks -- please either tell me how to have per-instance attributes as needed, or else log a bug report that this "feature" is still a problem in NT8. There are times when it is impossible to set the attribute value at compile time (because each derived class needs a different value) so it has to be set at run time -- which does not work due to the attribute sharing.

          --EV

          P.S. I now can and do hide properties (and could in NT7), but that no longer has anything to do with changing their value or any attribute value -- I just remove them from the collection in GetProperties().

          P.S. I also can and do hide lines and plots by simply not adding any that are not wanted -- the only complication is ensuring that the user's configuration settings get propagated, even for removed lines and plots (so you can hide a line, and then bring it back with the same settings).
          Last edited by ETFVoyageur; 09-08-2015, 01:34 PM.

          Comment


            #6
            Originally posted by ETFVoyageur View Post
            Thanks for the reference. I was hoping that things had gotten fixed in NT8. I guess not.

            It seems to me to be pretty darned sad that each indicator instance gets its own copy of the property itself, but any attributes of that property are shared. The traditional design is to share a value, but to fork (no longer share) when an instance writes to that value (i.e. copy-on-write). There needs to be some way to prevent an attribute being shared when that sharing is unwanted. It seems to me to be a pretty poor design where two independent instances are forced to have a shared value.

            Support folks -- please either tell me how to have per-instance attributes as needed, or else log a bug report that this "feature" is still a problem in NT8. There are times when it is impossible to set the attribute value at compile time (because each derived class needs a different value) so it has to be set at run time -- which does not work due to the attribute sharing.

            --EV

            P.S. I now can and do hide properties (and could in NT7), but that no longer has anything to do with changing their value or any attribute value -- I just remove them from the collection in GetProperties().

            P.S. I also can and do hide lines and plots by simply not adding any that are not wanted -- the only complication is ensuring that the user's configuration settings get propagated, even for removed lines and plots (so you can hide a line, and then bring it back with the same settings).
            If you create a local copy of the property, it will not get overwritten. It is kind of a duplication, I know, but that is the design of inheritance in OOP. So make a local declaration of the property in the indicator. After all, it will only be for some cases that you would need to force the override, so to speak.

            Comment


              #7
              That is a possibility. There are also a couple of other possibilities I am looking at.

              One possibility is to see whether instead of setting the attribute I can dynamically replace it with a new attribute instance. That would effectively give me copy-on-write. That would actually be the best answer if I can make it work.

              Another possibility is to have the base class provide distinct properties for the cases where changing an attribute is needed. That would be easy enough for me to do. I can easily hide the unwanted ones for any given indicator. The advantage this has over a local property is that all base class code for such properties would still apply. Not as good as copy-on-write, because it would be per-indicator, not per-instance (think of the case of two instances of the same indicator for a single chart). That is good enough for my current issue, although not adequate in general.

              What we really need is for NT to do it correctly and make attributes, at least property attributes, be copy-on-write.

              --EV
              Last edited by ETFVoyageur; 09-08-2015, 03:18 PM.

              Comment


                #8
                Originally posted by ETFVoyageur View Post
                ...

                Another possibility is to have the base class provide distinct properties for the cases where changing an attribute is needed. That would be easy enough for me to do. I can easily hide the unwanted ones for any given indicator. The advantage this has over a local property is that all base class code for such properties would still apply. Not as good as copy-on-write, because it would be per-indicator, not per-instance (think of the case of two instances of the same indicator for a single chart). That is good enough for my current issue, although not adequate in general.

                --EV
                You mean creating multiple properties each using the same backing store?

                Comment


                  #9
                  First let me say that this is evidently a C# issue, not a NinjaTader thing, and therefore not anything NT can fix. After some thought, here is how it looks to me:
                  • An attribute is a class, which is a reference type. C# seems to have all instances of an attribute on a given property be a reference to the same attribute instance. In my case that means all instances of a base class property. No matter which indicator is being instantiated, the property is still baseclass.property and all such refer to one single attribute instance.
                  • My next thought was to just create a new instance of the attribute and replace the property's instance, effectively giving me copy-on-write semantics. That fails, though. I can create the new instance of the attribute, but I have no way to place it in the property instance. The method where this occurs has no way to get a handle on the actual property. Even if it did, a quick look shows me no way to overwrite the property's existing attribute.
                  • Reflection does not help because all that does is get me information about the property, not the property itself. I can get either a PropertyInfo or a PropertyDescriptor, but not the property itself. What I do get is enough to get/set the property value.
                  • I can also use the property information to get the attribute I want, and set/get its values. That works because the property info object has a reference to the same attribute instance the property itself has.
                  • I cannot do what I need to do, though -- I cannot replace the property's reference to the attribute with a newly-created one that will affect no one else. I can only replace the reference in the property information, not in the property itself. Doing that is, of course, useless.

                  Looks like it is Plan B unless someone sees something I am overlooking.

                  --EV
                  Last edited by ETFVoyageur; 09-08-2015, 07:07 PM.

                  Comment


                    #10
                    Originally posted by koganam View Post
                    You mean creating multiple properties each using the same backing store?
                    I mean that if A and B both need to modify the same attribute on a given property, or if one does but the other cannot take that modification, then I'll create two base class properties. A will use one of them, B will use the other, and I'll ensure that only the correct property is shown in the GUI for each of them. That way I can still get the normal base class property support.

                    It's a hack, but it is the best thought I have had since I no longer believe that doing my own copy-on-write is likely to be possible.

                    --EV

                    Comment


                      #11
                      Originally posted by ETFVoyageur View Post
                      That is a possibility. There are also a couple of other possibilities I am looking at.

                      One possibility is to see whether instead of setting the attribute I can dynamically replace it with a new attribute instance. That would effectively give me copy-on-write. That would actually be the best answer if I can make it work.

                      Another possibility is to have the base class provide distinct properties for the cases where changing an attribute is needed. That would be easy enough for me to do. I can easily hide the unwanted ones for any given indicator. The advantage this has over a local property is that all base class code for such properties would still apply. Not as good as copy-on-write, because it would be per-indicator, not per-instance (think of the case of two instances of the same indicator for a single chart). That is good enough for my current issue, although not adequate in general.

                      What we really need is for NT to do it correctly and make attributes, at least property attributes, be copy-on-write.

                      --EV
                      And what happens when you write indicators C, D, E and F a few weeks from now, and they all do something with that property?

                      Still sounds better to me to simply have local copies in each of them, and leave well enough alone in the baseClass.

                      Comment


                        #12
                        I hear you. That thought is on my mind. I'm balancing several issues.

                        --EV

                        Comment

                        Latest Posts

                        Collapse

                        Topics Statistics Last Post
                        Started by algospoke, 04-17-2024, 06:40 PM
                        6 responses
                        49 views
                        0 likes
                        Last Post algospoke  
                        Started by arvidvanstaey, Today, 02:19 PM
                        4 responses
                        11 views
                        0 likes
                        Last Post arvidvanstaey  
                        Started by samish18, 04-17-2024, 08:57 AM
                        16 responses
                        61 views
                        0 likes
                        Last Post samish18  
                        Started by jordanq2, Today, 03:10 PM
                        2 responses
                        10 views
                        0 likes
                        Last Post jordanq2  
                        Started by traderqz, Today, 12:06 AM
                        10 responses
                        21 views
                        0 likes
                        Last Post traderqz  
                        Working...
                        X