Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>The overhead of throwing exceptions probably dwarfs the overhead of XML parsing. You need to rewrite your code so that it doesn't throw exceptions.</p> <p>One way is to check for the existence of an element before you ask for its value. That will work, but it's a lot of code. Another way to do it would be to use a map:</p> <pre><code>Dictionary&lt;string, string&gt; map = new Dictionary&lt;string, string&gt; { { "matchtype", null }, { "matches", null }, { "ballsbowled", null } }; foreach (XmlElement elm in stats.SelectNodes("*")) { if (map.ContainsKey(elm.Name)) { map[elm.Name] = elm.InnerText; } } </code></pre> <p>This code will handle all the elements whose names you care about and ignore the ones you don't. If the value in the map is null, it means that an element with that name didn't exist (or had no text).</p> <p>In fact, if you're putting the data into a <code>DataTable</code>, and the column names in the <code>DataTable</code> are the same as the element names in the XML, you don't even need to build a map, since the <code>DataTable.Columns</code> property is all the map you need. Also, since the <code>DataColumn</code> knows what data type it contains, you don't have to duplicate that knowledge in your code:</p> <pre><code>foreach (XmlElement elm in stats.SelectNodes("*")) { if (myTable.Columns.Contains(elm.Name)) { DataColumn c = myTable.Columns[elm.Name]; if (c.DataType == typeof(string)) { myRow[elm.Name] = elm.InnerText; continue; } if (c.DataType == typeof(double)) { myRow[elm.Name] = Convert.ToDouble(elm.InnerText); continue; } throw new InvalidOperationException("I didn't implement conversion logic for " + c.DataType.ToString() + "."); } } </code></pre> <p>Note how I'm not declaring any variables to store this information in, so there's no chance of me screwing up and declaring a variable of a data type different from the column it's stored in, or creating a column in my table and forgetting to implement the logic that populates it.</p> <p><strong>Edit</strong></p> <p>Okay, here's something that's a bit tricksy. This is a pretty common technique in Python; in C# I think most people still think there something weird about it.</p> <p>If you look at the second example I gave, you can see that it's using the metainformation in the <code>DataColumn</code> to figure out what logic to use for converting an element's value from text to its base type. You can accomplish the same thing by building your own map, e.g.:</p> <pre><code>Dictionary&lt;string, Type&gt; typeMap = new Dictionary&lt;string, Type&gt; { { "matchtype", typeof(string) }, { "matches", typeof(int) }, { "ballsbowled", typeof(int) } } </code></pre> <p>and then do pretty much the same thing I showed in the second example:</p> <pre><code>if (typeMap[elm.Name] == typeof(int)) { result[elm.Name] = Convert.ToInt32(elm.Text); continue; } </code></pre> <p>Your results can no longer be a <code>Dictionary&lt;string, string&gt;</code>, since now they can contain things that aren't strings; they have to be a <code>Dictionary&lt;string, object&gt;</code>. </p> <p>But that logic seems a little ungainly; you're testing each item several times, there are <code>continue</code> statements to break out of it - it's not terrible, but it could be more concise. How? By using another map, one that maps types to conversion functions:</p> <pre><code>Dictionary&lt;Type, Func&lt;string, object&gt;&gt; conversionMap = new Dictionary&lt;Type, Func&lt;string, object&gt;&gt; { { typeof(string), (x =&gt; x) }, { typeof(int), (x =&gt; Convert.ToInt32(x)) }, { typeof(double), (x =&gt; Convert.ToDouble(x)) }, { typeof(DateTime), (x =&gt; Convert.ToDateTime(x) } }; </code></pre> <p>That's a little hard to read, if you're not used to lambda expressions. The type <code>Func&lt;string, object&gt;</code> specifies a function that takes a <code>string</code> as its argument and returns an object. And that's what the values in that map are: they're lambda expressions, which is to say functions. They take a string argument (<code>x</code>), and they return an object. (How do we know that <code>x</code> is a string? The <code>Func&lt;string, object&gt;</code> tells us.)</p> <p>This means that converting an element can take one line of code:</p> <pre><code>result[elm.Name] = conversionMap[typeMap[elm.Name]](elm.Text); </code></pre> <p>Go from the inner to the outer expression: this looks up the element's type in <code>typeMap</code>, and then looks up the conversion function in <code>conversionMap</code>, and calls that function, passing it <code>elm.Text</code> as an argument.</p> <p>This may not be the ideal approach in your case. I really don't know. I show it here because there's a bigger issue at play. As Steve McConnell points out in <em>Code Complete</em>, it's easier to debug data than it is to debug code. This technique lets you turn program logic into data. There are cases where using this technique vastly simplifies the structure of your program. It's worth understanding.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload