Как справиться с изменением типа полезной нагрузки в C#

#c#

#c#

Вопрос:

Я пришел из мира JavaScript / TypeScript и немного изучаю C #, просто для удовольствия. В настоящее время я пытаюсь сделать то, что я очень привык делать в JS, то есть иметь полезную нагрузку, которая может изменяться в соответствии с переменной внутри класса. Итак, я попробовал следующее:

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

Итак, в этом примере я хочу знать, какой тип payload основан на type . Мне было интересно, какие будут альтернативы использованию dynamic в этом случае, поскольку я просматривал некоторые статьи, и в них упоминается, что мне следует избегать использования dynamic как можно больше.

Дело в том, что я не знаю, как реализовать это любым другим способом, и хотел бы получить некоторые рекомендации. Я был бы очень признателен за любые советы или примеры.

Комментарии:

1. Когда вы узнаете, какой тип payloud вы получите? изменится ли это в течение срока службы экземпляра команды?

2. Если вы знаете тип при создании команды, вы можете использовать learn.microsoft.com/de-de/dotnet/csharp/programming-guide /…

3. Тип берется из десериализатора, и его тип не должен меняться в течение срока службы экземпляра команды.

4. Единственная информация, которая говорит мне, какого типа будет полезная нагрузка, — это строка, имя type которой находится внутри объекта command

5. Payload<T> Payload { get; set; } должно сработать … но мне также было бы любопытно, как будут выглядеть некоторые полезные нагрузки. Дополнительные мысли, JS допускает множество вещей, которые, даже если их можно взломать в серверной части, являются плохой практикой.

Ответ №1:

Простым способом сделать это было бы определить полезную нагрузку как объект, а затем использовать сериализацию. Существует множество сериализаторов, поэтому выберите тот, который лучше всего подходит для вас.

 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 );
    }
}
  

Тогда, если ввод текста важен, вы могли бы сделать что-то вроде этого.

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

и используйте это так.

 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;
    }          
}
  

Комментарии:

1. Все решения отлично работают. Я использовал сочетание каждого из них и придумал свое решение. Определение Command и Command<T> вместе с new T Payload было настоящим трюком. Все сработало, у меня есть не только что-то достаточно общее, но и типизированное. Приветствия!

Ответ №2:

Это слишком долго и подробно для комментария, но я считаю, что это направление, в котором вы движетесь, поэтому я его напечатаю.

Сначала определения классов и интерфейсов:

 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;
    }
}
  

Выше наш интерфейс показывает, как должен выглядеть объект, если это «команда».
Наша команда использует преимущества дженериков: теоретически она любого типа.

«Человек» — это пользовательская полезная нагрузка.

В использовании 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"
            }
        }
    };
    
  

Я создаю список ICommand с различными типами.

Если мы хотим увидеть значения, которые мы можем сделать:

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

который будет регистрировать:

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

Я хотел, чтобы этот длинный пример продемонстрировал пару вещей.

  1. Вы можете заставить классы контролировать большую часть вашей логики. И подобная логика if (obj.Type == 'MyType') не является строго необходимой.
  2. Мы все еще можем воспользоваться преимуществами ввода и запрошенной вами логики:

Пример:

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

Ответ №3:

Есть несколько способов сделать это

  1. Шаблон команды

Я узнал об этом шаблоне в книге Head First Design Patterns: http://umass-cs-220.github.io/weeks/04/08-command.pdf

Основная идея заключается в том, что вы создаете ICommand интерфейса, который определяет функцию выполнения. Классы, которые реализуют интерфейс ICommand, имеют поле частной полезной нагрузки, и execute знает, как работать с данными частной полезной нагрузки. Намного чище, но требует от вас переосмысления вашего дизайна.

  1. Отражение Используйте метод Type, чтобы получить тип объекта и выполнить преобразование объекта полезной нагрузки в нужный вам тип. Не так чисто, как шаблон команды, но, возможно, проще для понимания.

Пример:

 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}");
            }

        }
    }
}