在C#泛型中无效?

dir*_*ion 86 c# generics

我有一个接受请求并提供响应的通用方法.

public Tres DoSomething<Tres, Treq>(Tres response, Treq request)
{/*stuff*/}
Run Code Online (Sandbox Code Playgroud)

但我并不总是希望对我的请求做出回应,而且我并不总是希望提供请求数据以获得响应.我也不想完整地复制和粘贴方法以进行微小的更改.我想要的是能够做到这一点:

public Tre DoSomething<Tres>(Tres response)
{
    return DoSomething<Tres, void>(response, null);
}
Run Code Online (Sandbox Code Playgroud)

这在某种程度上是可行的吗?似乎专门使用void不起作用,但我希望找到类似的东西.

das*_*ght 85

你不能使用void,但你可以使用object:它有点不方便,因为你的void功能需要返回null,但如果它统一你的代码,它应该是一个很小的代价.

这种无法void用作返回类型至少部分地负责一般代表Func<...>Action<...>一系列代表之间的分离:如果有可能返回void,一切Action<X,Y,Z>都会变得简单Func<X,Y,Z,void>.不幸的是,这是不可能的.

  • **(笑话)**他仍然可以通过`返回System.Runtime.Serialization.FormatterServices.GetUninitializedObject(typeof(void));`返回那些将成为无效方法的`void`.不过,这将是一个盒装空白. (42认同)
  • 由于 C# 支持更多函数式编程特性,您可以查看 FP 中表示 `void` 的 [Unit](https://github.com/louthy/language-ext#void-isnt-a-real-type)。并且有充分的理由使用它。在 F#(仍然是 .NET)中,我们内置了“unit”。 (3认同)
  • @joe 在这些情况下,我的做法是在需要的地方添加 `public readonly struct Nothing {}` 。 (2认同)

Jon*_*eet 81

不,不幸的是没有.如果void是"真实"类型(例如unit在F#中),生活在很多方面会简单得多.特别是,我们不会同时需要Func<T>Action<T>家庭-有好多只是Func<void>代替Action,Func<T, void>而不是Action<T>

它也会使异步变得更简单 - 根本不需要非泛型Task类型 - 我们只需要Task<void>.

不幸的是,这不是C#或.NET类型系统的工作方式......

  • "不幸的是,这不是C#或.NET类型系统的工作方式......"你让我充满希望,也许事情可能会最终发挥作用.你的最后一点意味着我们不可能有这样的事情吗? (4认同)
  • @Sahuagin:我怀疑不是 - 在这一点上这将是一个非常大的变化. (2认同)
  • 将Unit引用类型设置为空值类型以表示"void"是否有优势?空值类型似乎更适合我,它没有任何价值,也没有空间.我想知道为什么没有像这样实现虚空.弹出或不从堆栈弹出它将没有任何区别(谈论本机代码,在IL中可能会有所不同). (2认同)
  • @Ondrej:我之前尝试过使用空结构,但它最终在 CLR 中不是空的......当然,它可能是特殊情况。除此之外,我不知道我会建议哪一个;我还没有想太多。 (2认同)

Tri*_*Gao 25

这是你可以做的.正如@JohnSkeet所说,C#中没有单位类型,所以自己动手吧!

public sealed class ThankYou {
   private ThankYou() { }
   private readonly static ThankYou bye = new ThankYou();
   public static ThankYou Bye { get { return bye; } }
}
Run Code Online (Sandbox Code Playgroud)

现在你可以随时使用Func<..., ThankYou>而不是Action<...>

public ThankYou MethodWithNoResult() {
   /* do things */
   return ThankYou.Bye;
}
Run Code Online (Sandbox Code Playgroud)

或者使用Rx团队已经制作的东西:http://msdn.microsoft.com/en-us/library/system.reactive.unit%28v=VS.103%29.aspx

  • 将ThankYou重命名为“ KThx”,这是一个赢家。^ _ ^`Kthx.Bye;` (5认同)

Jep*_*sen 15

您可以Object像其他人建议的那样使用.或者Int32我看到了一些用途.使用Int32引入了一个"虚拟"数字(使用0),但至少你不能将任何大而奇特的对象放入Int32引用(结构被密封).

你也可以写自己的"void"类型:

public sealed class MyVoid
{
  MyVoid()
  {
    throw new InvalidOperationException("Don't instantiate MyVoid.");
  }
}
Run Code Online (Sandbox Code Playgroud)

MyVoid允许引用(它不是静态类)但只能是null.实例构造函数是私有的(如果有人试图通过反射调用此私有构造函数,则会抛出异常).

  • @Aelphaeis 这个概念与空对象模式有点不同。在这里,重点只是拥有某种可以与泛型一起使用的类型。null 对象模式的目标是避免编写返回 null 的方法来指示特殊情况,而是返回具有适当默认行为的实际对象。 (3认同)
  • 另一个名称是空对象模式(设计模式). (2认同)

Esk*_*ahn 6

我喜欢上面的Aleksey Bykov的想法,但它可以简化一点

public sealed class Nothing {
    public static Nothing AtAll { get { return null; } }
}
Run Code Online (Sandbox Code Playgroud)

因为我没有明显的理由为什么Nothing.AtAll不能只给null

同样的想法(或Jeppe Stig Nielsen的想法)对于使用类型类也很有用.

例如,如果类型仅用于描述作为某个方法的参数传递的过程/函数的参数,并且它本身不接受任何参数.

(你仍然需要制作一个虚拟包装器或允许一个可选的"Nothing".但恕我直言,类使用看起来很好用myClass <Nothing>)

void myProcWithNoArguments(Nothing Dummy){
     myProcWithNoArguments(){
}
Run Code Online (Sandbox Code Playgroud)

要么

void myProcWithNoArguments(Nothing Dummy=null){
    ...
}
Run Code Online (Sandbox Code Playgroud)

  • 值“null”具有缺失或不存在的“对象”的含义。“Nothing”只有一个值意味着它永远不会像其他任何东西一样。 (2认同)