Announcement

Collapse

Looking for a User App or Add-On built by the NinjaTrader community?

Visit NinjaTrader EcoSystem and our free User App Share!

Have a question for the NinjaScript developer community? Open a new thread in our NinjaScript File Sharing Discussion Forum!
See more
See less

Partner 728x90

Collapse

Serialization and custom Clone() cause dupilicate copy problem.

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

    Serialization and custom Clone() cause dupilicate copy problem.

    According to this link https://ninjatrader.com/support/help...lor_inputs.htm the way around serializing custom types and complex types is to create a non-browsable string property for the member to serialize.

    The code from the example abovee is


    Code:
    [XmlIgnore()]
    [Display(Name[COLOR=#ffffff] [/COLOR]=[COLOR=#ffffff] [/COLOR][COLOR=#800000]"BorderBrush"[/COLOR],[COLOR=#ffffff] [/COLOR]GroupName[COLOR=#ffffff] [/COLOR]=[COLOR=#ffffff] [/COLOR][COLOR=#800000]"NinjaScriptParameters"[/COLOR],[COLOR=#ffffff] [/COLOR]Order[COLOR=#ffffff] [/COLOR]=[COLOR=#ffffff] [/COLOR][COLOR=#ff6600]0[/COLOR])]
    
    [COLOR=#0000ff]public [/COLOR]Brush[COLOR=#ffffff] [/COLOR]BorderBrush
    {[COLOR=#ffffff] [/COLOR]get;[COLOR=#ffffff] [/COLOR]set;[COLOR=#ffffff] [/COLOR]}
    
    [Browsable([COLOR=#0000ff]false[/COLOR])]
    [COLOR=#0000ff]public[/COLOR][COLOR=#ffffff] [/COLOR][COLOR=#0000ff]string[/COLOR][COLOR=#ffffff] [/COLOR]BorderBrushSerialize
    {
      get[COLOR=#ffffff] [/COLOR]{[COLOR=#ffffff] [/COLOR][COLOR=#0000ff]return[/COLOR][COLOR=#ffffff] [/COLOR]Serialize.BrushToString(BorderBrush);[COLOR=#ffffff] [/COLOR]}
    [COLOR=#ffffff]  [/COLOR]set[COLOR=#ffffff] [/COLOR]{[COLOR=#ffffff] [/COLOR]BorderBrush[COLOR=#ffffff] [/COLOR]=[COLOR=#ffffff] [/COLOR]Serialize.StringToBrush(value);[COLOR=#ffffff] [/COLOR]}
    }
    The base.Clone() method is going to copy both BorderBrush and BorderBrushSeralize properties when it executes. This results in the BorderBrush being set twice, once with the less efficient serialization code.

    This becomes further complicated if the object in question requires custom code in the Clone() handler. Consider the following.

    Code:
    class Foo
    {
        int someCustomData;
        double moreCustomData;
    
        // Additional members, code to manage object foo and handle properties.
    }
    
    class MyIndicator : IndicatorBase
    {
    
        Foo someCustomObject;
    
        [Browsable(false)]
        public string FooSerialize
        {
            get { return someCustomData != null ? someCustomObject.SerializeToString() : string.Empty; }
            set
            { someCustomObject = Foo.SerializeFromString(value); }
        }
    
        public override object Clone()
        {
            // save the original foo object
            Foo originalCustomData = someCustomData;
    
            // if we don't set someCustomData to null it will be serialized and deserialized when FooSeralize is copied by the clone method
            someCustomData = null;
    
            // If we don't use the clone method provided by the base, then we are responsible for copying everything? Its not clear how best
            // to go about copying all the various things needed to properly copy an Indicator
            var result = base.Clone();
    
            // Restore the originalCustomData back.  // If an exception is thrown by base.Clone() this will be lost.  Need to figure out a way to
            // mitigate this.
            someCustomData = originalCustomData;
    
            // now do our custom cloning
            if(result != null)
            {
                result.someCustomData = someCustomData.SomeCustomCloneMethod();
            }
        }
    
    }
    It is inefficient to allow someCustomData to be copied via Serialization.

    How does one prevent a serialization property from being copied by the Clone() method?

    #2
    Hello ntbone,

    I don't believe that there would be a way to filter the property in the Clone override specifically, this would be where the total object is cloned. Per the description what you are describing is expected, or that all properties are copied along with all browsable properties. You can construct a new object in the clone with only the properties you wanted copied however you would need to do that yourself similar to the what is done in the @StrategyGenerator.cs file.

    Are you seeing a specific problem surrounding something you are doing here or just trying to find ways to improve performance?

    I look forward to being of further assistance.
    JesseNinjaTrader Customer Service

    Comment


      #3
      I am looking for a bit of clarity on the documentation and more information on what the Clone() implementation does. The documentation is somewhat confusing as to what the Clone() method does.

      To clarify the clone method
      • Copies all public properties (regardless of the [Browsable(false)] flag
      • Doesn't copy private/protected properties
      • There's no way to mark a property as public, but ignored by the Clone() method

      Comment


        #4
        Hello ntbone,

        The points you noted seem correct however a clone is not the same as an access modifier so all members of the class would be cloned, public or private. The Clone method is mainly used to produce a cloned instance of the object which retains the same values, mainly used for constructing a new object in a custom way if needed. The platform handles this on its own, however in some advanced use cases this may be needed to create an object.

        You can see this used in a few locations in the existing NinjaScript objects, mainly the more complex items such as PriceLevels or the @StrategyGenerator.cs. You can also read about the IClonable interface which is being used in the platform: https://docs.microsoft.com/en-us/dot...tframework-4.5

        I look forward to being of further assistance.
        JesseNinjaTrader Customer Service

        Comment


          #5
          Ah. So then to re-clarify with this new information
          • Copies all properties regardless of it being public, protected, or private (regardless of the [Browsable(false)] attribute.
          • No way to mark a property as public, but ignored by the Clone() method.
          • NinjaScript objects implement IClonable interface.

          The link provided includes this line of text "Because callers of Clone() cannot depend on the method performing a predictable cloning operation, we recommend that ICloneable not be implemented in public APIs."

          It's not clear if private members are copied as well or if this pertains to just properties?

          According to this link https://ninjatrader.com/support/help...lightsub=state on the NinjaScript life cycle, cloning is a common part of the process for objects being created. You mention "in a custom way if needed" but I see this clone method being called quite often in practice.

          I have been dealing with some pretty hard to track down bugs in part because my object includes a complex type that needs to be serialized. Prior to custom cloning methods and serialization there were strange bugs happening that were very difficult to track down.

          It would be nice if there was a method to serialize data associated with an object that would not be subject to being part of the cloning process.

          Concerns
          • Performance impact of serialized properties being part of the clone process.
          • Side effects of the clone process without the code change from my example above.
          Consider the following two code variations

          Code:
          class Foo
          {
              int someCustomData;
              double moreCustomData;
          
              public void Parse(string input);
          
              // Additional members, code to manage object foo and handle properties.
          
          }
          
          class MyIndicator : IndicatorBase
          {
          
              private Foo someCustomObject;
          
              [XmlIgnore]
              [Browsable(false)]
              public Foo SomeCustomObject { get { return someCustomObject; } { set someCustomObject = value; } }
          
              [Browsable(false)]
              public string FooSerialize
              {
                  get { return someCustomData != null ? someCustomObject.SerializeToString() : string.Empty; }
                  set
                  {
                      someCustomData.Parse(value);
                  }
              }
          
              public override object Clone()
              {
                  var result = base.Clone();
          
                  // **** Without or without the current implementation of FooSeralize, result.someCustomObject refers to the exact
                  // same instance as this.someCustomObject ****
              }
          }
          Code:
          class Foo
          {
              int someCustomData;
              double moreCustomData;
          
              // Additional members, code to manage object foo and handle properties.
          }
          
          class MyIndicator : IndicatorBase
          {
          
              Foo someCustomObject;
          
              [XmlIgnore]
              [Browsable(false)]
              public Foo SomeCustomObject { get { return someCustomObject; } { set someCustomObject = value; } }
          
              [Browsable(false)]
              public string FooSerialize
              {
                  get { return someCustomData != null ? someCustomObject.SerializeToString() : string.Empty; }
                  set
                  { someCustomObject = Foo.SerializeFromString(value); }
              }
          
              public override object Clone()
              {
                  var result = base.Clone();
          
              // Depending on the order that SomeCustomObject and FooSeralize are copied either
              // result.someCustomObject will refer to a newly created unique object
                  //    OR
                  // result.someCustomObject will be a reference to the exact same instance as this.someCustomObject as
              // If FooSeralize happens first, and SomeCustomObject is copied second, SomeCustomObject will wipe out the object
              // created by FooSearlize
              }
          }
          Neither of the above side effects are obvious to developers. All of these problems are caused because of the method of Serialization available. The solution I presented at the beginning, is the only one I have been able to come up with that safely prevents these problems from occurring.

          It would be highly desirable to have some way to serialize data that is done via functions instead of properties. Something as simple as

          Code:
              virtual string WriteCustomData();
              virtual void ReadCustomData(string);
          
          // or since XML is used to store these objects
          
              virtual XElement ConvertToXML();
              virtual void ConertFromXML(XElement element);
          I would strongly suggest updating the documentation for both Clone and the Serialization example you provide in your help to make developers aware of the potential pitfalls of storing custom objects in a NinjaScript object.

          Comment

          Latest Posts

          Collapse

          Topics Statistics Last Post
          Started by funk10101, Today, 12:02 AM
          1 response
          11 views
          0 likes
          Last Post NinjaTrader_LuisH  
          Started by GLFX005, Today, 03:23 AM
          1 response
          6 views
          0 likes
          Last Post NinjaTrader_Erick  
          Started by nandhumca, Yesterday, 03:41 PM
          1 response
          13 views
          0 likes
          Last Post NinjaTrader_Gaby  
          Started by The_Sec, Yesterday, 03:37 PM
          1 response
          11 views
          0 likes
          Last Post NinjaTrader_Gaby  
          Started by vecnopus, Today, 06:15 AM
          0 responses
          1 view
          0 likes
          Last Post vecnopus  
          Working...
          X