If you really want to make this functional, it looks like a case for the "Maybe monad". See
Unfortunately the dichotomy of class
vs struct
limits the elegance of Maybe monads in C#, but with the following:
public static class Maybe
{
public static Maybe<TElement> ToMaybe<TElement>(this TElement value) where TElement : class
{
return new Maybe<TElement>(value);
}
}
public struct Maybe<TElement> where TElement : class
{
private readonly TElement _Wrapped;
public bool HasValue { get { return _Wrapped != null; } }
public TElement Value
{
get
{
if (_Wrapped == null) throw new InvalidOperationException();
return _Wrapped;
}
}
public Maybe(TElement value)
{
_Wrapped = value;
}
public TResult? SelectStruct<TResult>(Func<TElement, TResult> fn) where TResult : struct
{
return _Wrapped == null ? default(TResult?) : fn(_Wrapped);
}
// Returns "this" for fluent usage
public Maybe<TElement> Exec(Action<TElement> ifAction, Action elseAction)
{
if (_Wrapped != null) ifAction(_Wrapped);
else elseAction();
return this;
}
}
you can refactor
var subElement = (from res in doc.Descendants(_xmlns + "subelements")
where res.Element(_xmlns + "object")
.Element(_xmlns + "metadata")
.Element(_xmlns + "identifier").Value == "1"
select res).FirstOrDefault();
var offsetElement = subElement.Descendants(_xmlnsEc + "start").Select(x => x.Element(_xmlnsEc + "editUnitNumber")).FirstOrDefault();
if (offsetElement == null)
{
Logger.Error<SubtitleNormalizer>("no offset found");
return 0;
}
return Int32.Parse(offsetElement.Value);
to
return (from res in doc.Descendants(_xmlns + "subelements")
where res.Element(_xmlns + "object")
.Element(_xmlns + "metadata")
.Element(_xmlns + "identifier").Value == "1"
select res).
FirstOrDefault().
Descendants(_xmlnsEc + "start").
Select(x => x.Element(_xmlnsEc + "editUnitNumber")).
FirstOrDefault().
ToMaybe().
Exec(val => {}, () => { Logger.Error<SubtitleNormalizer>("no offset found"); }).
SelectStruct(elt => Int32.Parse(elt.Value)).
GetValueOrDefault();
A fuller Maybe
implementation might have a Where
method, a Select<TResult>(Func<TElement, TResult>) where TResult : class
method, and a GetValueOrDefault()
method, and extension methods to do the same for Nullable<TStruct>
, but I've simplified it to just support what's needed for this answer.