locked
DataMemberContructor Problems... RRS feed

  • Question

  • So I want to do the following:

     

    Code Block

    [DataContract]

    [DataMemberConstructor]

    public class IMUPacket

    {

    public IMUPacket(){ }

     

    public IMUPacket(double[] packet)

    {

    if (packet.Length == 10)

    {

    int i = 0;

    magX = packet[i++];

    magY = packet[i++];

    magZ = packet[i++];

    accelX = packet[i++];

    accelY = packet[i++];

    accelZ = packet[i++];

    rotX = packet[i++];

    rotY = packet[i++];

    rotZ = packet[i++];

    LRollOver = packet[i];

    }

    }

     

    //Gyro-Stabilized Magnetic Field Vectors (x, y, z)

    [DataMember]

    public double magX;

    [DataMember]

    public double magY;

    [DataMember]

    public double magZ;

    //Gyro-Stabilized Acceleration Vectors (x, y, z)

    [DataMember]

    public double accelX;

    [DataMember]

    public double accelY;

    [DataMember]

    public double accelZ;

    //Bias-Compensated Angular Rate Vectors (x, y, z)

    [DataMember]

    public double rotX;

    [DataMember]

    public double rotY;

    [DataMember]

    public double rotZ;

    //seconds since last rollover

    [DataMember]

    public double LRollOver;

    }

     

     

    However, when I proceed to use this class, I am offered two constructors, the empty constructor, and one which allows me to specify all 10 data members in the constructor. How am I able to achieve the above functionality? It seems to me that specifying each individual value using:

     

    variable = new WhateverType();

    variable.member1 = blah1;

    variable.member2 = blah2;

    ...

     

    is just as verbose and cumbersome as:

     

    variable = new WhateverType(blah1, blah2, ...);

     

    Consider the case where WhateverType consists of 50 Data member variables! Imagine having to specify that constructor in multiple places in code? what a nightmare. I really really hope there is a way to implement something like what I have above.

     

    Thanks,

    Don

    Wednesday, December 12, 2007 6:04 AM

Answers

  • Here is how I worked around the problem. It's a roughly comparable solution in terms of lines of code and ease of implementation, but it would still be great to have this functionality in the constructor.

     

    Contract:

     

    Code Block

    [DataContract]

    public class IMUPacket

    {

    //Gyro-Stabilized Magnetic Field Vectors (x, y, z)

    [DataMember]

    public double magX;

    [DataMember]

    public double magY;

    [DataMember]

    public double magZ;

     

    //Gyro-Stabilized Acceleration Vectors (x, y, z)

    [DataMember]

    public double accelX;

    [DataMember]

    public double accelY;

    [DataMember]

    public double accelZ;

     

    //Bias-Compensated Angular Rate Vectors (x, y, z)

    [DataMember]

    public double rotX;

    [DataMember]

    public double rotY;

    [DataMember]

    public double rotZ;

     

    //seconds since last rollover

    [DataMember]

    public double LRollOver;

     

    // bool valid packet

    [DataMember]

    public bool valid;

    }

     

    Helper Function... a static method that can be called like a constructor:

     

    Code Block

    public static class IMUHelper

    {

    public static IMUPacket IMUPacketCreate(double[] packet)

    {

    IMUPacket imupacket = new IMUPacket();

    imupacket.valid = false;

     

    if (packet.Length == 10)

    {

    int i = 0;

    imupacket.magX = packet[i++];

    imupacket.magY = packet[i++];

    imupacket.magZ = packet[i++];

    imupacket.accelX = packet[i++];

    imupacket.accelY = packet[i++];

    imupacket.accelZ = packet[i++];

    imupacket.rotX = packet[i++];

    imupacket.rotY = packet[i++];

    imupacket.rotZ = packet[i++];

    imupacket.LRollOver = packet[i++];

    imupacket.valid = true;

    }

     

    return imupacket;

     

    }

    }

     

     

     

    implementation:

     

    Code Block

    public void IMUPacketHandler(double[] rawData)

    {

    packet = IMUHelper.IMUPacketCreate(rawData);

    ...

    }

     

     

    -Don
    Friday, December 14, 2007 2:53 AM

All replies

  • What about another data structure? If you put your values in some list or dictionary type, you could use a loop to set the values in the constructor.

     

    Wednesday, December 12, 2007 9:24 AM
  • How about this:

    [DataMember]
    [DataMemberConstructor]
    class IMUPacket {

    double[] _data;

      [DataMember]
     double[] Data { get; set; }

    [DataContract]
    enum Field { MAGX=0, MAGY=1 ...}

    }

    ... and replace references to foo.magX with foo.Data[IMUPacket.Field.MAGX];

    More code per line but possibly more convenient for you, without the overhead of serializing a dictionary.






    Wednesday, December 12, 2007 4:52 PM
  • Some further thoughts on this...

    It would be very cool if dssproxy emitted a constructor of the form  Foo(Dictionary<string,object> args) which initializes all the members that happen to be defined in the dictionary. I'm assuming it would be easy for dssproxy to map strings to fields. Then any permutation of member fields and default initializers could be applied.  That doesn't quite solve Don's problem but it would still be handy. :-)
    Wednesday, December 12, 2007 6:56 PM
  • Rob, while I appreciate your attempt to help, neither of your solutions provide a reasonable solution. I don't want to use lists or collections or dictionaries... I just want to be able to access constructors in the proxy DLL! This is not an unreasonable request.

     

    I think I misunderstood originally what the [DataMemberConstructor] tag was going to do for me. It makes perfect sense now, it creates a constructor with ALL of the data members present.

     

    I therefore submit another complaint to George and request that some additional constructor functionality be added to the 2.0 CTP. I just can't understand the logic behind not including the capability to do what I am trying to implement in the top post.

     

    - (a very frustrated) Don

    Thursday, December 13, 2007 12:07 AM
  •  

    I think it would be very difficult for dssproxy to always do the right thing if you asked it to implement all the constructor signatures you define in your class.  For one, it would need to understand how to convert your double[] into the various member fields.   Maybe you could supply an attribute tag that asked dssproxy to copy your constructor code verbatim, in which case it would have to check that the code makes reference only to [DataMember] fields and doesn't touch private or internal fields.

     

    In the meantime I think the simplest route to take is to create some helper functions that do the double[] to proxy packet class conversion for you.

     

    cheers,

    R

    Thursday, December 13, 2007 12:19 AM
  •  Rob Sim -- Braintech wrote:

     

    Maybe you could supply an attribute tag that asked dssproxy to copy your constructor code verbatim, in which case it would have to check that the code makes reference only to [DataMember] fields and doesn't touch private or internal fields.

     

     

    Yes! This would be just perfect!

     

    Even if it was added in 2.0, I couldn't wait that long anyway, so I will in fact be using the helper function route as you suggested in the meantime. I will place my helper functions into a universal namespace which is accessible to all of my services.

     

    Thanks Rob!

     

    -Don

    Thursday, December 13, 2007 12:28 AM
  • Here is how I worked around the problem. It's a roughly comparable solution in terms of lines of code and ease of implementation, but it would still be great to have this functionality in the constructor.

     

    Contract:

     

    Code Block

    [DataContract]

    public class IMUPacket

    {

    //Gyro-Stabilized Magnetic Field Vectors (x, y, z)

    [DataMember]

    public double magX;

    [DataMember]

    public double magY;

    [DataMember]

    public double magZ;

     

    //Gyro-Stabilized Acceleration Vectors (x, y, z)

    [DataMember]

    public double accelX;

    [DataMember]

    public double accelY;

    [DataMember]

    public double accelZ;

     

    //Bias-Compensated Angular Rate Vectors (x, y, z)

    [DataMember]

    public double rotX;

    [DataMember]

    public double rotY;

    [DataMember]

    public double rotZ;

     

    //seconds since last rollover

    [DataMember]

    public double LRollOver;

     

    // bool valid packet

    [DataMember]

    public bool valid;

    }

     

    Helper Function... a static method that can be called like a constructor:

     

    Code Block

    public static class IMUHelper

    {

    public static IMUPacket IMUPacketCreate(double[] packet)

    {

    IMUPacket imupacket = new IMUPacket();

    imupacket.valid = false;

     

    if (packet.Length == 10)

    {

    int i = 0;

    imupacket.magX = packet[i++];

    imupacket.magY = packet[i++];

    imupacket.magZ = packet[i++];

    imupacket.accelX = packet[i++];

    imupacket.accelY = packet[i++];

    imupacket.accelZ = packet[i++];

    imupacket.rotX = packet[i++];

    imupacket.rotY = packet[i++];

    imupacket.rotZ = packet[i++];

    imupacket.LRollOver = packet[i++];

    imupacket.valid = true;

    }

     

    return imupacket;

     

    }

    }

     

     

     

    implementation:

     

    Code Block

    public void IMUPacketHandler(double[] rawData)

    {

    packet = IMUHelper.IMUPacketCreate(rawData);

    ...

    }

     

     

    -Don
    Friday, December 14, 2007 2:53 AM
  • Note that there is a little documented feature that lets you pick and choose which parameters are in the auto-generated constructor.  See code:

    Code Block

        [DataMemberConstructor]
        [DataContract]
        public class Foo
        {
            [DataMemberConstructor(Order = 1)]
            [DataMember]
            public int First;

            [DataMemberConstructor(Order = 2)]
            [DataMember]
            public int Second;

            [DataMemberConstructor(Order = -1)]
            [DataMember]
            public int LeaveMeOut;
        }



    Also, i think one of the reasons we have proxy services is to remove things like methods and constructors.  This creates a strict separation between contract and behavior.  

    Friday, December 14, 2007 2:53 PM
  • Don,  I'm not sure if you've noticed, but the 1.5 refresh adds a flag to dssproxy to include extra code to compile into the service. (/ext:<path>) I haven't tried it but it looks like your problem may be solved.  Kudos to the MSRS team for this addition. :-)
    Monday, December 17, 2007 4:45 PM
  • Right, this extension ties back to DssNewService where if you use the /ExtendProxy command line argument then it will automatically set up dssproxy to include it. You can find a description of how to use this in our online help [1]. Once you have created a project with DssNewservice using the /ExtendProxy command line argument you will find two changes:

     

    1) There now is a file called <sample>_Extension.cs which you can use as a starting point for adding code to the proxy.

     

    2) The post build step looks slightly different:

     

    Code Block
        <PostBuildEvent>"C:\Microsoft Robotics Studio (1.5)\bin\dssproxy.exe" /dll:"$(TargetPath)" /proxyprojectpath:"$(ProjectDir)Proxy " /keyfile:"$(AssemblyOriginatorKeyFile)" $(ProxyDelaySign) $(CompactFrameworkProxyGen) /binpath:". " /referencepath:"C:\Microsoft Robotics Studio (1.5)\bin\ " /referencepath:"C:\Microsoft Robotics Studio (1.5)\bin\ " /partialclass+ /ext:"C:\Microsoft Robotics Studio (1.5)\mysample " </PostBuildEvent>

     

    You can incorporate this into an existing project to get the same functionality into your existing service projects.

     

    Henrik

     

    [1] http://msdn2.microsoft.com/en-us/library/bb483017.aspx

     

    Wednesday, December 19, 2007 12:13 AM
  • Hi Henrik,

     

    I've been trying to create a generic contract with some "helper" functions that are included as static methods in the Proxy. I can put them in the main DLL, but then you have trouble using them due to the difference between proxy types and the main types.

     

    I keep getting an error from DssProxy: exited with error 10.

     

    I tried to follow the instructions in the online documentation, and I found that it did not work either. Here are the steps to repro the problem. Maybe I am doing something that is just not possible:

     

    1. dssnewservice /s:TestExtend /extendproxy

    2. Open the new service. Compile it. Everything is fine.

    3. Edit AssemblyInfo.cs and change the service declaration to:

    [assembly: ServiceDeclaration(DssServiceDeclaration.DataContract)]

    This is because I want a generic contract. I have also tried it with a logical OR with ServiceBehavior.

    4. Delete TestExtend.cs because there is no service associated with the contract, only the extra methods that are supposed to live in TestExtendService_Extension.cs.

    5. Try to compile, and it fails with error 10.

     

    Any suggestions?

     

    It seems to me that having helper functions for manipulating data types in your generic contract is a fairly important feature. I know you can create a separate DLL that uses the proxy and then reference that DLL, but why do we have to have two DLLs to do one job?

     

    Thanks,

    Trevor

    Tuesday, January 29, 2008 10:37 AM