简体   繁体   English

如何在 C# 中处理不同的负载类型

[英]How to deal with varying payload type in C#

I come from the JavaScript/TypeScript world and I'm learning some C#, just for fun.我来自 JavaScript/TypeScript 世界,我正在学习一些 C#,只是为了好玩。 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.我目前正在尝试做一些我非常习惯在 JS 中做的事情,它有一个可以根据类中的变量改变的有效负载。 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 .所以,在这个例子中,我想知道哪种类型的payload基于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.我想知道在这种情况下使用dynamic替代方法是什么,因为我正在查看一些文章,他们提到我应该尽可能避免使用dynamic

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.我真的很感激任何提示或示例。

An easy way to do this would be to define the payload as Object and then use serialization.一种简单的方法是将有效负载定义为 Object,然后使用序列化。 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;
    }          
}

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我在 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.基本思想是创建一个接口 ICommand 来定义执行函数。 The classes that implement the ICommand interface have a private payload field and execute knows how to work with the private payload data.实现 ICommand 接口的类有一个私有负载字段,execute 知道如何处理私有负载数据。 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.反射 使用 Type 方法获取对象类型并将有效负载对象转换为您需要的类型。 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}");
            }

        }
    }
}

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:在 C# 中的用法:

    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.我正在创建各种类型的 ICommand 列表。

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.而像if (obj.Type == 'MyType')这样的逻辑并不是绝对必要的。
  2. We can still take advantage of typing, and the logic you requested:我们仍然可以利用打字和您请求的逻辑:

Example:例子:

 if (command.Type == typeof(Person))
 {
    // Do things, with the stuff
 }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM