0

I come from the JavaScript/TypeScript world and I'm learning some C#, just for fun. I am currently trying to do something I am very used to do in JS, which is having a payload that can change according to a variable inside a class. So, I tried the following:

namespace Shared.Commands
{
    public class Command
    {
        public string clientUuid { get; set; }
        public string type { get; set; }
        public dynamic payload { get; set; }
    }
}

So, in this example, I want to know which type payload is based on the type. I was wondering what would be alternatives to using dynamic in this case, as I was looking at some articles and they mention that I should avoid using dynamic as much as possible.

The point is: I am clueless as to how to implement this in any other way and would like some guidance. I would really appreciate any tips or examples.

rvitorper
  • 45
  • 7
  • When do you know what payloud type you get? will it change in the lifetime of the Command instance? – Patrick Artner Oct 29 '20 at 12:53
  • In case you know the type on creation of the command, you could use https://learn.microsoft.com/de-de/dotnet/csharp/programming-guide/generics/ – Patrick Artner Oct 29 '20 at 12:54
  • The type comes from a deserializer and its type should not change in the lifetime of the Command instance. – rvitorper Oct 29 '20 at 12:58
  • The only information that tells me what type the payload would be is the string named ```type``` inside the command object – rvitorper Oct 29 '20 at 12:59
  • https://stackoverflow.com/questions/223952/create-an-instance-of-a-class-from-a-string – Patrick Artner Oct 29 '20 at 13:00
  • I'm wondering if there is any way to preserve the data that payload contains. I looked at the reflection examples and most of them require you to know the values that payload contains. At the moment I get the command, I don't know what is in there. In JS I'd just do ```payload.speed``` but how can I do something similar in C# and avoid using dynamic? – rvitorper Oct 29 '20 at 13:12
  • 1
    `Payload Payload { get; set; }` should work... but I'd also be curious what some of the payloads would look like. Additional thoughts, JS allows a lot of things that even if hackable in the backend, are bad practices. – Austin T French Oct 29 '20 at 13:51

3 Answers3

1

An easy way to do this would be to define the payload as Object and then use serialization. There are tons of serializers out there so pick the one that works best for you.

public class Command
{
    public string ClientUuid { get; set; }
    public string Type { get; set; }
    public Object Payload { get; set; }

    public static void Serialize ( Command command, MemoryStream stream )
    {
        var formatter = new BinaryFormatter ();
        formatter.Serialize ( stream, command );
    }

    public static void Deserialize (out Command command, MemoryStream stream )
    {
        var formatter = new BinaryFormatter();
        command = (Command)formatter.Deserialize ( stream );
    }
}

Then if typing is important, you could do something like this.

public class Command<T> : Command
{
    public new T Payload
    {
        get
        {
            return (T)base.Payload;
        }
        set
        {
            base.Payload = (T)value;
        }
    }
}

and use it like this.

public void Usage ()
{
    Command<YourObject> obj = new Command<YourObject> () {
        Payload = new YourObject ()
    };

    using ( var stream = new MemoryStream () )
    {
        Command.Serialize ( obj, stream );

        // do something with serialized data in stream;
    }          
}
Dan Barrett
  • 225
  • 1
  • 6
  • All solutions work greatly. I used a mix of each and came up with my solution. Defining the ```Command``` and ```Command``` along with the ```new T Payload``` was the real trick. Everything worked I have not only something sufficiently generic but also typed. Cheers! – rvitorper Oct 30 '20 at 12:43
0

There are several ways to do this

  1. Command pattern

I learned about this pattern in the book Head First Design Patterns: http://umass-cs-220.github.io/weeks/04/08-command.pdf

The basic idea is that you create an interface ICommand that defines an execute function. The classes that implement the ICommand interface have a private payload field and execute knows how to work with the private payload data. Much cleaner but requires you to rethink your design.

  1. Reflection Use the Type method to get the object type and do a conversion of the payload object to the type you need. Not as clean as the command pattern but maybe easier to understand.

Example:

namespace MyStackExchangeExamplesConsole
{
     public class Command
        {
            public string clientUuid { get; set; }
            public object payload { get; set; }
        }

    class Program
    {

        static void Main(string[] args)
        {
            var command1 = new Command()
            {
                clientUuid = "UUID1",
                payload = new string("I am a string")
            };
            var command2 = new Command()
            {
                clientUuid = "UUID2",
                payload = (double)5.0 
            };

            DoSomethingWithTheCommand(command1);
            DoSomethingWithTheCommand(command2);
        }

        static void DoSomethingWithTheCommand(Command cmd)
        {

            if (cmd.payload is string)
            {
                var s = (string)cmd.payload;
                Console.WriteLine($"I'm a string ! {s}");
            }
            else if (cmd.payload is double)
            {
                var d = (double)cmd.payload;
                Console.WriteLine($"I'm a double ! {d}");
            }

        }
    }
}
Austin T French
  • 5,022
  • 1
  • 22
  • 40
JVSC
  • 1
  • 2
0

This is too long and detailed for a comment, but I believe this is the direction you are headed so I'll type it out.

First the class and interface definitions:

public interface ICommand
{
    string ClientUuid { get; set; }
    Type Type { get; }
    object Payload { get; set; }
}

public class Command<T> : ICommand
{
    public string ClientUuid { get; set; }
    public Type Type 
    { 
        get 
        {   
            return Payload.GetType();
        }
                    
    }
    public object Payload { get; set; }
}

public class Person
{
    public string Name {get; set;}
    public string Address { get; set; }
    public override string ToString()
    {
        return Name + " is at " + Address;
    }
}

Above, our interface provides what an object should look like if it is a 'command'. Our command takes advantage of generics: being theoretically any type.

A "Person" is a custom payload.

In C# usage:

    List<ICommand> commands = new List<ICommand>()
    {
        new Command<int>()
        {
            ClientUuid = "a-uuid",          
            Payload = 15            
        },
        new Command<string>()
        {
            ClientUuid = "a-uuid",          
            Payload = "we has string"           
        },
        new Command<Person>()
        {
            ClientUuid = "A person UUID",
            Payload = new Person()
            {
                Name = "Tom",
                Address = "123 Main St"
            }
        }
    };
    

I'm creating a list of ICommand with various types.

If we wanted to see the values we can do:

    foreach(ICommand command in commands)
    {
        Console.WriteLine(command.Type);
        Console.WriteLine(command.Payload.ToString());
    }

which would log:

System.Int32
15
System.String
we has string
Person
Tom is at 123 Main St

I wanted this long example to demonstrate a couple of things.

  1. You can have the classes control most of your logic. And logic like if (obj.Type == 'MyType') isn't strictly necessary.
  2. We can still take advantage of typing, and the logic you requested:

Example:

 if (command.Type == typeof(Person))
 {
    // Do things, with the stuff
 }
Austin T French
  • 5,022
  • 1
  • 22
  • 40