Reputation: 2741
I have this peace of code. I have to check each level in XML avoiding NULL exception. Can I improve this code?
private void GetFunctionsValues(XElement x)
{
if (x.Element("credentials") != null)
{
if (x.Element("credentials").Element("faccess") != null)
{
foreach (var access in x.Element("credentials").Element("faccess").Elements("access"))
{
if (access.Attribute("db_id") != null)
{
if (int.Parse(access.Attribute("db_id").Value) == 19) FuncPlus = true;
}
}
}
}
}
Upvotes: 2
Views: 163
Reputation: 8832
I believe this would be the simplest alternative:
private void GetFunctionsValues(XElement x)
{
var el = x.XPathSelectElement(@"credentials/faccess/access[@db_id=19]");
if(el != null)
FuncPlus = true;
}
XPathSelectElement
is declared in System.Xml.XPath
namespace.
Upvotes: 3
Reputation: 33242
You can collapse the two nested if
in a single one:
if (x.Element("credentials") != null && x.Element("credentials").Element("faccess") != null )
The first one evaluating to false prevent the execution of the second, so there is no null reference exceptions. This behavior is usually called "short circuit evaluation": at soon the program understand it can skip the if
, it stops evaluating the remaining part.
Upvotes: 5
Reputation: 8540
I have an extension method to make such code more readable:
public static TResult Access<TSource, TResult>(
this TSource obj,
Func<TSource, TResult> expression,
TResult defaultValue = default(TResult)) where TSource : class
{
if (obj == null)
{
return defaultValue;
}
return expression.Invoke(obj);
}
In such case you can write fluently access some property:
var faccessElement = x
.Access(y => y.Element("credentials"))
.Access(y => y.Element("faccess"));
if (faccessElement != null)
{
//...
}
Upvotes: 2
Reputation: 13148
if it was me I'd write it something like this:
private void GetFunctionsValues(XElement x)
{
if (x.Element("credentials") == null
|| x.Element("credentials").Element("faccess") == null)
return;
bool any =
x
.Element("credentials")
.Element("faccess")
.Elements("access")
.Where(access => access.Attribute("db_id") != null)
.Any(access => int.Parse(access.Attribute("db_id").Value) == 19);
if (any)
FuncPlus = true;
}
Key things to note:
two if
s in succession can be replaced by joining their expressions with &&
. Ex:
if (test1)
if (test2)
// becomes
if (text1 && test2)
a foreach
followed by an if
can be replaced with a LINQ Where
query. Ex:
foreach (var item in collection)
if (item.SomeProperty == someValue)
action(item);
// becomes
collection
.Where(item => item.SomeProperty == someValue)
.ForEach(action);
tests that you make before performing some action are often better written inverted to avoid excessive nesting. Ex:
if (test1)
if (test2)
if (test3)
doSomething();
// becomes
if (!(test1 || test2 || test3))
return;
doSomething();
Upvotes: 1
Reputation: 382092
I often find code more readable with return instead of imbrication :
private void GetFunctionsValues(XElement x)
{
if (x.Element("credentials") == null || x.Element("credentials").Element("faccess") == null) return;
foreach (var access in x.Element("credentials").Element("faccess").Elements("access"))
{
if (access.Attribute("db_id") != null && int.Parse(access.Attribute("db_id").Value) == 19)
{
FuncPlus = true;
return; // it seems we can leave now !
}
}
}
And look at the return I added when the condition is verified : I don't think you need to continue the iteration.
Upvotes: 1
Reputation: 9869
To Improve it and Combine two if, you can try
private void GetFunctionsValues(XElement x)
{
var credentialsElement = x.Element("credentials") ;
if (credentialsElement != null && credentialsElement.Element("faccess") != null)
{
foreach (var access in credentialsElement.Element("faccess").Elements("access"))
{
if (access.Attribute("db_id") != null)
{
if (int.Parse(access.Attribute("db_id").Value) == 19) FuncPlus = true;
}
}
}
}
Upvotes: 1
Reputation: 174289
You could at least make it a little bit more compact by combining the first two if
s into one and putting the selection of the attribute into the query in the foreach
loop:
private void GetFunctionsValues(XElement x)
{
if (x.Element("credentials") != null
&& x.Element("credentials").Element("faccess") != null)
{
foreach (var attribute in x.Element("credentials")
.Element("faccess")
.Elements("access")
.Select(x => x.Attribute("db_id"))
.Where(x => x != null))
{
if (int.Parse(attribute.Value) == 19) FuncPlus = true;
}
}
}
Please see this answer to learn why you can combine the first two if
s into one.
Upvotes: 1
Reputation: 36487
You can merge a few of the if()
s simply due to the fact that boolean operations have a defined order their operands are evaluated (left to right):
Instead of writing this:
if (condition_a)
if (condition_b)
action();
You can simply use this:
if (condition_a && condition_b)
action();
Just make sure you still verify your object exists first, then perform further checks.
Upvotes: 1