Void в С# generics?

У меня есть общий метод, который принимает запрос и предоставляет ответ.

public Tres DoSomething<Tres, Treq>(Tres response, Treq request)
{/*stuff*/}

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

public Tre DoSomething<Tres>(Tres response)
{
    return DoSomething<Tres, void>(response, null);
}

Насколько это возможно? Похоже, что конкретное использование void не работает, но я надеюсь найти что-то аналогичное.

Ответ 1

Вы не можете использовать void, но можете использовать object: это немного неудобно, потому что ваши функции be-be- void должны возвращать null, но если он унифицирует ваш код, он должен быть небольшая цена для оплаты.

Эта неспособность использовать void как возвращаемый тип, по меньшей мере, частично отвечает за разделение между семействами Func<...> и Action<...> общих делегатов: было ли возможно вернуть void, all Action<X,Y,Z> станет просто Func<X,Y,Z,void>. К сожалению, это невозможно.

Ответ 2

Нет, к сожалению нет. Если бы void были "реальным" типом (например, unit в F #), жизнь была бы намного проще во многих отношениях. В частности, нам не нужны семейства Func<T> и Action<T> - вместо Action, Func<T, void> вместо Action<T> и т.д. Вместо Action<T> и т.д. Вместо Func<void>.

Это также упростит асинхронность - вообще не будет необходимости в универсальном типе Task - у нас будет только Task<void>.

К сожалению, это не так, как работают системы типа С# или .NET.

Ответ 3

Вот что вы можете сделать. Как сказал @JohnSkeet, в С# нет типа юнитов, так что сделайте это сами!

public sealed class ThankYou {
   private ThankYou() { }
   private readonly static ThankYou bye = new ThankYou();
   public static ThankYou Bye { get { return bye; } }
}

Теперь вы всегда можете использовать Func<..., ThankYou> вместо Action<...>

public ThankYou MethodWithNoResult() {
   /* do things */
   return ThankYou.Bye;
}

Или используйте что-то уже сделанное командой Rx: http://msdn.microsoft.com/en-us/library/system.reactive.unit%28v=VS.103%29.aspx

Ответ 4

Вы можете просто использовать Object, как предлагали другие. Или Int32, который я видел некоторое использование. Использование Int32 вводит "фиктивное" число (используйте 0), но, по крайней мере, вы не можете поместить большой и экзотический объект в ссылку Int32 (структуры запечатаны).

Вы также можете написать свой собственный тип void:

public sealed class MyVoid
{
  MyVoid()
  {
    throw new InvalidOperationException("Don't instantiate MyVoid.");
  }
}

Ссылки MyVoid разрешены (это не статический класс), но могут быть только null. Конструктор экземпляра является закрытым (и если кто-то попытается вызвать этот закрытый конструктор с помощью отражения, в них будет выдано исключение).


Поскольку были введены кортежи значений (2017,.NET 4.7), может быть естественным использовать struct ValueTuple (0-кортеж, неуниверсальный вариант) вместо такого MyVoid. Его экземпляр имеет ToString(), который возвращает "()", поэтому он выглядит как нулевой кортеж. Начиная с текущей версии С#, вы не можете использовать токены () в коде для получения экземпляра. Вместо этого вы можете использовать default(ValueTuple) или просто default (когда тип может быть выведен из контекста).

Ответ 5

Мне нравится идея Алексея Быкова выше, но она может быть упрощена немного

public sealed class Nothing {
    public static Nothing AtAll { get { return null; } }
}

Поскольку я не вижу никакой очевидной причины, почему Nothing.AtAllAllAllAll не может просто дать null

То же самое (или Jeppe Stig Nielsen) также отлично подходит для использования с типизированными классами.

например. если тип используется только для описания аргументов процедуры/функции, переданных в качестве аргумента для некоторого метода, и сам он не принимает никаких аргументов.

(Вам все равно нужно либо сделать фиктивную оболочку, либо разрешить необязательное "Nothing". Но IMHO использование класса выглядит красиво с myClass <Nothing> )

void myProcWithNoArguments(Nothing Dummy){
     myProcWithNoArguments(){
}

или

void myProcWithNoArguments(Nothing Dummy=null){
    ...
}

Ответ 6

void, хотя тип, действителен только как возвращаемый тип метода.

Нет ограничений по этому ограничению void.