Ответ
Такая задача возникает при работе с необобщёнными (IEnumerable) или сырыми коллекциями. Реализация требует осторожности из-за потери безопасности типов.
Способ 1: Использование необобщённого IEnumerable (наиболее прямой)
Просто перебираем коллекцию, возвращая последний элемент или null.
public static object LastOrDefault(IEnumerable sequence)
{
if (sequence == null) throw new ArgumentNullException(nameof(sequence));
object lastElement = null;
foreach (var item in sequence)
{
lastElement = item;
}
return lastElement; // Для пустой коллекции вернёт null
}
Способ 2: Обобщённый метод с динамическим определением типа (более гибкий)
Позволяет вернуть результат в ожидаемом типе, но использует dynamic с потебой проверки во время выполнения.
public static T LastOrDefault<T>(IEnumerable sequence)
{
if (sequence == null) throw new ArgumentNullException(nameof(sequence));
// Пытаемся привести последовательность к IEnumerable<T>
if (sequence is IEnumerable<T> typedSequence)
{
return typedSequence.LastOrDefault(); // Используем встроенный LINQ
}
// Если приведение не удалось, перебираем вручную
T last = default(T);
foreach (var item in sequence)
{
// Это вызовет RuntimeBinderException, если тип item не приводим к T
last = (T)item; // Небезопасное приведение!
}
return last;
}
// Использование: var last = LastOrDefault<string>(someCollection);
Способ 3: Использование рефлексии для вызова обобщённого метода LINQ Мощно, но сложно и медленно. Подходит для библиотечного кода.
public static object LastOrDefault(IEnumerable sequence, Type elementType)
{
var methodInfo = typeof(Enumerable)
.GetMethod(nameof(Enumerable.LastOrDefault), BindingFlags.Public | BindingFlags.Static)
.MakeGenericMethod(elementType); // Создаём обобщённый метод Enumerable.LastOrDefault<T>
return methodInfo.Invoke(null, new object[] { sequence });
}
Практические рекомендации:
- Избегайте
dynamicи рефлексии, если есть возможность переработать код для использования обобщённых типов (IEnumerable<T>). Это обеспечит безопасность типов и производительность. - Если тип коллекции неизвестен, но известен тип элемента, используйте Способ 2 с явным приведением и обработкой исключений.
- Для полностью универсального подхода (когда неизвестно ничего) Способ 1 — самый безопасный, но он возвращает
object, и ответственность за приведение типа ложится на вызывающий код.