Як я можу повернути NULL із загального методу в C #?


546

У мене є загальний метод з цим (фіктивним) кодом (так, я знаю, що в IList є предикати, але мій код не використовує IList, а якусь іншу колекцію, все-таки це питання не має значення для питання ...)

static T FindThing<T>(IList collection, int id) where T : IThing, new()
{
    foreach T thing in collecion
    {
        if (thing.Id == id)
            return thing;
    }
    return null;  // ERROR: Cannot convert null to type parameter 'T' because it could be a value type. Consider using 'default(T)' instead.
}

Це дає мені помилку побудови

"Неможливо перетворити нульовий параметр типу" T ", тому що це може бути тип значення. Розглянемо натомість використання" default (T) "."

Чи можу я уникнути цієї помилки?


Чи будуть кращі рішення для цього зараз нульові довідкові типи (в C # 8)? docs.microsoft.com/en-us/dotnet/csharp/nullable-references Повертаючись nullнезалежно від того , Tє Objectчи intабо char.
Олександр - Відновіть Моніку

Відповіді:


968

Два варіанти:

  • Повернення, default(T)що означає, що ви повернетесь, nullякщо T - тип посилання (або тип нульового значення), 0для int, '\0'для charтощо ( таблиця значень за замовчуванням (C # Посилання) )
  • Обмежте T, щоб бути еталонним типом з where T : classобмеженням, а потім поверніться nullяк звичайне

3
Що робити, якщо мій тип повернення - перерахунок, а не клас? Я не можу вказати T: enum :(
Джастін

1
У .NET enum - дуже тонка (і досить герметична) обгортка навколо цілого типу. Умовою є використання нуля для значення "перерахування" за замовчуванням.
Майк Чемберлен

27
Я думаю, що проблема в цьому полягає в тому, що якщо ви використовуєте цей загальний метод, щоб сказати, перетворіть об'єкт Бази даних з DbNull в Int і він поверне за замовчуванням (T), де T є int, він поверне 0. Якщо це число насправді має сенс, тоді ви обходите погані дані у випадках, коли це поле було недійсним. Або кращим прикладом може бути DateTime. Якщо поле було чимось на зразок "DateClosed" і воно було повернуто як нулеве, оскільки обліковий запис все ще відкритий, він фактично за замовчуванням (DateTime) до 1/1/0000, що означає, що обліковий запис було закрито до винайдення комп'ютерів.
Сінестетик

21
@Sinaesthetic: Отже, ви переходите до цього Nullable<int>чи Nullable<DateTime>замість нього. Якщо ви використовуєте ненульовий тип і вам потрібно представляти нульове значення, ви просто задаєте собі проблеми.
Джон Скіт

1
Я згоден, я просто хотів це виховувати. Я думаю, що я робив більше схожий на MyMethod <T> (); припустити, що це нерегульований тип і MyMethod <T?> (); припустити, що це нульовий тип. Тож у моїх сценаріях я міг би використовувати змінну temp, щоб зловити нуль та піти звідти.
Синестетик

84
return default(T);

Це посилання: msdn.microsoft.com/en-us/library/xwth0h0d(VS.80).aspx має пояснити, чому.
Харпер Шелбі

1
Чорт забирай, я б заощадив багато часу, якби знав про це ключове слово - дякую Рікардо!
Ана Беттс

1
Я здивований, що це не набрало більше голосів, оскільки ключове слово "за замовчуванням" є більш комплексним рішенням, яке дозволяє використовувати нереференційні типи в поєднанні з числовими типами та структурами. Хоча прийнята відповідь вирішує проблему (і справді є корисною), вона краще відповідає, як обмежити тип повернення до змінних / посилальних типів.
Стів Джексон

33

Ви можете просто скорегувати свої обмеження:

where T : class

Тоді повернення null дозволено.


Дякую. Я не можу вибрати 2 відповіді як прийняте рішення, тому я вибираю причину Джона Скіта, оскільки його відповідь має два рішення.
edosoft

@Migol це залежить від ваших вимог. Можливо, їх проект цього вимагає IDisposable. Так, більшість часу це не повинно бути. System.Stringне реалізує IDisposable, наприклад. Відповідач повинен був уточнити це, але це не робить неправильну відповідь. :)
ahwm

@Migol У мене немає поняття, чому я мав IDisposable там. Вилучено.
TheSoftwareJedi

13

Додайте обмеження класу як перше обмеження до свого загального типу.

static T FindThing<T>(IList collection, int id) where T : class, IThing, new()

Дякую. Я не можу вибрати 2 відповіді як прийняте рішення, тому я вибираю причину Джона Скіта, оскільки його відповідь має два рішення.
edosoft

7
  1. Якщо у вас є об'єкт, тоді вам потрібно набрати текст

    return (T)(object)(employee);
  2. якщо вам потрібно повернути null.

    return default(T);

Привіт user725388, будь ласка, перевірте перший варіант
Jogi Joseph George

7

Нижче наведено два варіанти, якими можна скористатися

return default(T);

або

where T : class, IThing
 return null;

6

Вашим іншим варіантом буде додати це до кінця декларації:

    where T : class
    where T: IList

Таким чином це дозволить повернути нуль.


Якщо обидва обмеження відносяться до одного типу, ви один раз згадуєте тип і використовуєте кому, як-от where T : class, IList. Якщо у вас є обмеження для різних типів, ви повторюєте маркер where, як в where TFoo : class where TBar : IList.
Джеппе Стіг Нільсен

3

рішення TheSoftwareJedi працює,

також ви можете заархівувати його за допомогою декількох типів значення та нульового значення:

static T? FindThing<T>(IList collection, int id) where T : struct, IThing
{
    foreach T thing in collecion
    {
        if (thing.Id == id)
            return thing;
    }
    return null;
}

1

Візьміть рекомендацію про помилку ... і будь-якого користувача default(T)або new T.

Вам доведеться додати до порівняння у свій код, щоб переконатися, що він був дійсним, якщо ви йдете цим маршрутом.

В іншому випадку потенційно розглянемо вихідний параметр "відповідність знайдена".


1

Ось робочий приклад повернення значень Nullable Enum:

public static TEnum? ParseOptional<TEnum>(this string value) where TEnum : struct
{
    return value == null ? (TEnum?)null : (TEnum) Enum.Parse(typeof(TEnum), value);
}

Оскільки C # 7.3 (травень 2018 року), ви можете покращити обмеження where TEnum : struct, Enum. Це гарантує, що абонент випадково не вводить тип значення, який не є перерахунком (наприклад, a intабо a DateTime).
Джеппе Стіг Нільсен

0

Ще одна альтернатива двом відповідям, представленим вище. Якщо ви змінили тип повернення на object, ви можете повернутися null, в той же час подати ненулеве повернення.

static object FindThing<T>(IList collection, int id)
{
    foreach T thing in collecion
    {
        if (thing.Id == id)
            return (T) thing;
    }
    return null;  // allowed now
}

Знизу: це вимагатиме від абонента методу віддати повернений об’єкт (у ненульовому випадку), що передбачає бокс -> менша продуктивність. Чи правий я?
Csharpest

0

Для повноти, добре знати, що ви також могли це зробити:

return default;

Він повертає те саме, що і return default(T);


0

Для мене це працює так, як є. Де саме проблема?

public static T FindThing<T>(this IList collection, int id) where T : IThing, new()
{
    foreach (T thing in collection)
    {
        if (thing.Id == id)
            return thing;
        }
    }

    return null; //work
    return (T)null; //work
    return null as T; //work
    return default(T); //work


}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.