简体   繁体   English

C# 强类型等效于泛型类型,其中 SomeClass<t, v> 和 SomeClass<v, t> 是相同的</v,></t,>

[英]C# strongly typed equivalent of a generic type, wherein SomeClass<T, V> and SomeClass<V, T> are the same

TL;DR : In C#, what is a good way to create DTOs for serialization/deserialization, wherein different, strongly-typed properties may be independently present or absent in different contexts, but with the DTOs themselves being strongly-typed and non-dynamic, and without manually creating a new class for each scenario ? TL;DR : 在 C# 中,什么是创建用于序列化/反序列化的 DTO 的好方法,其中不同的强类型属性可能在不同的上下文中独立存在或不存在,但 DTO 本身是强类型且非动态的,并且无需为每个场景手动创建新的 class


I'm running into what I think is a weak point in C#, but want to see if there's some way around this:我遇到了我认为 C# 的弱点,但想看看是否有解决办法:

For a current project, I'm going to be having multiple microservices communicating with each other, and each microservice will need zero or more things passed in through the body of an HTTP REST request.对于当前的项目,我将有多个微服务相互通信,每个微服务将需要零个或多个通过 HTTP REST 请求的主体传递的内容。 Some microservices will need the same sets of data, and some microservices will need to different sets.一些微服务需要相同的数据集,而一些微服务需要不同的数据集。

The stock way to do this would be to manually create a new DTO class for every scenario, then use those DTO types in parameters: [FromBody] SomeDtoType someData .执行此操作的常用方法是为每个场景手动创建一个新的 DTO class,然后在参数中使用这些 DTO 类型: [FromBody] SomeDtoType someData

However there's a problem in this case: I want to mix and match these different sets of data as desired, where each microservice method can just kind of pick and choose which sets it desires;但是在这种情况下存在一个问题:我想根据需要混合和匹配这些不同的数据集,其中每个微服务方法都可以选择它想要的数据集; and I want to do this without manually creating a separate class in each individual case.我想在不手动创建单独的 class 的情况下做到这一点。

So for example, imagine I have something like this:例如,想象一下我有这样的东西:

[Flags]
public enum InvocationArgTypes
{
    None = 0,
    Webhook = 1,
    DataSource = 2,
    ...
}

---

[DataContract(Name = "Webhook")]
public class WebhookDto
{
    ...
}

---

[DataContract(Name = "DataSource")]
public class DataSourceDto
{
    ...
}

This then is what I want to avoid :这就是我要避免的:

  1. InvocationArgTypes.None --> null InvocationArgTypes.None --> null

  2. InvocationArgTypes.Webhook --> WebhookDto InvocationArgTypes.Webhook --> WebhookDto

  3. InvocationArgTypes.DataSource --> DataSourceDto InvocationArgTypes.DataSource --> DataSourceDto

  4. InvocationArgTypes.Webhook & InvocationArgTypes.DataSource --> WebhookDataSourceHybridDto InvocationArgTypes.Webhook & InvocationArgTypes.DataSource --> WebhookDataSourceHybridDto

  5. ... ...

One of the main reasons is that, as the list InvocationArgTypes grows, having to account for each individual case of 2, 3, ..., n flags and manually create classes for every case isn't just time-consuming;主要原因之一是,随着InvocationArgTypes列表的增长,必须考虑 2、3、...、n 标志的每个单独案例并为每个案例手动创建类不仅耗时; it pollutes the type system with many awkwardly named classes, such as Type1Type2Type3Type4HybridDto .它用许多笨拙命名的类污染了类型系统,例如Type1Type2Type3Type4HybridDto

Now for the million-dollar question: Why not just use generics?现在是百万美元的问题:为什么不直接使用 generics? Because SomeGeneric<Type1, Type2> and SomeGeneric<Type2, Type1> are not the same.因为SomeGeneric<Type1, Type2>SomeGeneric<Type2, Type1>不一样。 In particular, it would be easy for two services to look at the same exact flags in that enum and accidentally order the same generic parameters differently.特别是,两个服务很容易在该枚举中查看相同的确切标志,并意外地以不同的方式排序相同的通用参数。

Normally that type of thing points to using dynamic types...but I really want to avoid that if possible.通常这种类型的东西指向使用动态类型......但如果可能的话,我真的想避免这种情况。

So does C# have a good solution to this problem?那么C#有没有很好的解决这个问题的办法呢? If so, what is it?如果是这样,它是什么? Or do I really need to settle on one of the options above?还是我真的需要选择上述选项之一?

Please note: It is acceptable in this case to always wrap every body in an overall, "grand DTO type", which contains all of the 0 or more sets of data specified by the flags enum as properties.请注意:在这种情况下,始终将每个主体包装在一个整体的“大 DTO 类型”中是可以接受的,该类型包含标志枚举指定的所有 0 个或多个数据集作为属性。 In other words, whether no flags are set or every flag is set, it is okay, if needed, to still use the same "grand DTO type" as a wrapper, if there's a good, clean way to do so.换句话说,无论是没有设置标志还是设置了每个标志,如果需要,仍然可以使用相同的“大 DTO 类型”作为包装器,如果有一个好的、干净的方法可以这样做。

This seems like an incredibly deep (and interesting,) question.这似乎是一个非常深刻(而且有趣)的问题。 and I'm not familiar enough with microservices to feel fully confident in offering a practical solution, However, I notice that you didn't mention conversion operators as one of your proposed solutions, so I need to use the space of this answer to share with you what's effectively an elongated comment on this topic.而且我对微服务还不够熟悉,无法对提供实用的解决方案充满信心,但是,我注意到您没有将转换运算符作为您提出的解决方案之一,因此我需要使用此答案的空间来分享与您一起,实际上是对该主题的细长评论。

Observe the below little toy program.观察下面的小玩具程序。 This shows the use of an implicit operator, which I learned of in this answer recently.这显示了隐式运算符的使用,这是我最近在这个答案中了解到的。 The operator converts a generic type by flipping the generic parameters:运算符通过翻转泛型参数来转换泛型类型:

using System;

public class Program
{
    public static void Main()
    {
        GenericDto<string, int> dto = new GenericDto<string, int>("Hello world", 42);
        StringIntUser siu = new StringIntUser();
        IntStringUser isu = new IntStringUser();
        Console.WriteLine(siu.print(dto));
        Console.WriteLine(isu.print(dto));
    }
}

class IntStringUser
{
    public string print(GenericDto<int, string> dto)
    {
        return string.Format("Int {0} is paired with string {1}", dto._t1, dto._t2);
    }
}

class StringIntUser
{
    public string print(GenericDto<string, int> dto)
    {
        return string.Format("String {0} is paired with int {1}", dto._t1, dto._t2);
    }
}

class GenericDto<T1, T2>
{
  public T1 _t1;
  public T2 _t2;  
  public GenericDto(T1 t1, T2 t2)
  {
    _t1 = t1; _t2 = t2;
  }
  public static implicit operator GenericDto<T1, T2>(GenericDto<T2, T1> dto) => new GenericDto<T1, T2>(dto._t2, dto._t1);
}

Now, as I mentioned in the preamble, I have no idea how practical this is for your use case.现在,正如我在序言中提到的,我不知道这对您的用例有多实用。 I can't really see how you're using the different DTOs.我真的看不出你是如何使用不同的 DTO 的。 In particular, I see the following limitations:特别是,我看到以下限制:

  • All the data needed to construct an instance of the generic class must be publicily available, so that we can re-order it.构建通用 class 实例所需的所有数据都必须公开,以便我们重新排序。
  • The number of implicit operators you need to define grows faster than exponentially with the number of generic parameters (which I presume correspond to members of your enum).您需要定义的隐式运算符的数量随着泛型参数的数量(我认为对应于您的枚举成员)的数量呈指数增长。 Specifically, there are n!具体来说,有n! (n factorial ) different orderings of n parameters, so we'd need to write that many conversion functions. (n阶乘) n参数的不同顺序,所以我们需要编写那么多转换函数。 That's a lot of functions to write.很多函数要写。 For just 10 generic params, we'd need around three and a half million functions.对于仅10通用参数,我们需要大约 350 万个函数。 However, I have a vague idea that there might be a clever hack using composition.但是,我有一个模糊的想法,即可能存在使用组合的巧妙技巧。 Only 50% confident.只有 50% 的信心。 Not going to try anything until I get more feedback on your use case.在我获得有关您的用例的更多反馈之前,不会尝试任何事情。
  • My very limited understanding of microservices is that they're all separate little programs communicating to each other via text.我对微服务的理解非常有限,它们都是通过文本相互通信的独立小程序。 So I'm wondering would the implicit operator need to be defined repeatedly in each microservice?所以我想知道隐式运算符是否需要在每个微服务中重复定义? Would this be a problem?这会是个问题吗?

Would you mind fleshing out your use case in the question?您介意在问题中充实您的用例吗? Specifically, could you show and end to end use case?具体来说,你能展示和端到端的用例吗? Is there any way you can disentangle the question form microservices and DTOs specifically or are they an essential component?有什么方法可以让你从微服务和 DTO 中解开问题,或者它们是必不可少的组成部分吗? Well, I'm assuming microservices can't be disentangled, based on this comment:好吧,我假设微服务无法解开,基于此评论:

...And it has to play nice with stock, run-of-the-mill serialization and deserialization: :) ...而且它必须与库存、普通的序列化和反序列化配合得很好::)

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

相关问题 C#:添加到类型SomeClass的列表 - C#: Adding to a List of Type SomeClass “SomeClass”之间有区别吗<t>其中T:SomeOtherClass”和“SomeClass<someotherclass> ()”?</someotherclass></t> - Is there a difference between "SomeClass<T> where T : SomeOtherClass" and "SomeClass<SomeOtherClass>()"? C#WPF DataGrid绑定IEnumerable <someclass> - C# WPF DataGrid bind IEnumerable<someclass> 需要转换清单 <T> 列出 <someClass> 然后回来 - Need to Convert List<T> to List<someClass> and Back 是否可以返回IQueryable <SomeClass> 一个IQueryable <T> 方法? - Is it possible to return IQueryable<SomeClass> to a IQueryable<T> method? 将泛型对象转换为强类型 T - Converting a generic object to a strongly typed T 可以转换SomeClass <T> 到SomeClass <object> 直到运行时T才“未知”? - Possible to convert SomeClass<T> to SomeClass<object> where T is not “known” until runtime? 为什么从SomeClass进行投射 <T> 其中T:BaseClass到SomeClass <DerivedClass : BaseClass> 不可能? - Why casting from SomeClass<T> where T : BaseClass to SomeClass<DerivedClass : BaseClass> is not possible? 获取实例 someClass<T1,T2> 给出错误&#39;...是一个变量,但像类型一样使用&#39; - Getting instance someClass<T1,T2> gives error '... is a variable but is used like a type' Type T = Type.GetType(“ SomeClass”); 给我一个对象? - How does Type T = Type.GetType(“SomeClass”); give me an object?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM