I want to parse only some data out of a ~100 MB rdf cell line ontology. So far I am interested in 169.796 out of 1.387.097 tripples (1.217.301 tripples being skipped).
I need ~24 seconds using the handler below to create the graph. This is only some seconds less then parsing the ontology in total.
Is there something I could improve in skipping the tuples I am not interested in?
Thanks!
private class MyHandler : VDS.RDF.GraphHandler
{
public MyHandler(IGraph g)
: base(g)
{
}
protected override bool HandleTripleInternal(Triple t)
{
if (t.Predicate is UriNode uri
&& uri.Uri.AbsoluteUri != "http://www.w3.org/2000/01/rdf-schema#subClassOf"
&& uri.Uri.AbsoluteUri != "http://www.w3.org/2000/01/rdf-schema#label")
{
return true;
}
else if (t.Object is LiteralNode l && l.Language == "zh")
{
return true;
}
return base.HandleTripleInternal(t);
}
}
To make the comparison of nodes a bit faster you could try comparing directly with a UriNode created from the graph instead of comparing URI strings. If you use the IGraph.CreateUriNode() method in your filter constructor to create nodes for rdfs:subClassOf and rdfs:label and then use IUriNode.Equals() as your comparator then you should find that the node comparison can use a faster object reference equality rather than a string comparison.
private class MyHandler : GraphHandler
{
private readonly IUriNode _rdfsSubClassOf;
private readonly IUriNode _rdfsLabel;
public MyHandler(IGraph g)
: base(g)
{
_rdfsSubClassOf = g.CreateUriNode(UriFactory.Create("http://www.w3.org/2000/01/rdf-schema#subClassOf"));
_rdfsLabel = g.CreateUriNode(UriFactory.Create("http://www.w3.org/2000/01/rdf-schema#label"));
}
protected override bool HandleTripleInternal(Triple t)
{
if (t.Predicate is UriNode uri
&& !uri.Equals(_rdfsSubClassOf)
&& !uri.Equals(_rdfsLabel))
{
return true;
}
else if (t.Object is LiteralNode l && l.Language == "zh")
{
return true;
}
return base.HandleTripleInternal(t);
}
}
However that is only going to speed up the filter and I suspect that if you profile the parse of the file you will find that the majority of the time is spent in parsing the syntax to create the triple that is passed to your filter. There isn't really a way to get around this issue in the dotNetRDF handler architecture.
Related
I'm comparing two graphs, one from a Turtle file with simple literal objects, the other from a file with explicit datatype IRIs. The graphs are otherwise equal.
Graph A:
<s> <p> "o"
Graph B:
<s> <p> "o"^^xsd:string
According to RDF 1.1 (3.3 Literals), "[s]imple literals are syntactic sugar for abstract syntax literals with the datatype IRI http://www.w3.org/2001/XMLSchema#string". This is reflected in the concrete syntax specifications as well (N-Triples, Turtle, RDF XML).
So I'd expect both my graphs to consists of a single triple with a URI node s subject, a URI node p predicate, and a literal node o with type xsd:string object. Based on this I'd expect there to be no difference between the two.
However this is not the case in practice:
var graphStringA = "<http://example.com/subject> <http://example.com/predicate> \"object\".";
var graphStringB = "<http://example.com/subject> <http://example.com/predicate> \"object\"^^<http://www.w3.org/2001/XMLSchema#string>.";
var graphA = new Graph();
var graphB = new Graph();
StringParser.Parse(graphA, graphStringA);
StringParser.Parse(graphB, graphStringB);
var diff = graphA.Difference(graphB);
There's one added and one removed triple in the difference report. The graphs are different, because the datatypes for the object nodes are different: graphA.Triples.First().Object.Datatype is nothing, while graphB.Triples.First().Object.Datatype is the correct URI.
It appears to me that to modify this behaviour I'd have to either
go all the way down to LiteralNode (and change its assumptions about literal nodes), or
create a new GraphDiff (that takes the default datatype of string literals into account).
A workaround is to remove the "default" datatypes:
private static void RemoveDefaultDatatype(IGraph g)
{
var triplesWithDefaultDatatype =
from triple in g.Triples
where triple.Object is ILiteralNode
let literal = triple.Object as ILiteralNode
where literal.DataType != null
where literal.DataType.AbsoluteUri == "http://www.w3.org/2001/XMLSchema#string" || literal.DataType.AbsoluteUri == "http://www.w3.org/2001/XMLSchema#langString"
select triple;
var triplesWithNoDatatype =
from triple in triplesWithDefaultDatatype
let literal = triple.Object as ILiteralNode
select new Triple(
triple.Subject,
triple.Predicate,
g.CreateLiteralNode(
literal.Value,
literal.Language));
g.Assert(triplesWithNoDatatype.ToArray());
g.Retract(triplesWithDefaultDatatype);
}
Is there a way in dotnetrdf to compare simple literals to typed literals in a way that's consistent with RDF 1.1, without resorting to major rewrite or workaround as above?
dotNetRDF is not RDF 1.1 compliant nor do we claim to be. There is a branch which is rewritten to be compliant but it is not remotely production ready.
Assuming that you control the parsing process you can customise the handling of incoming data using the RDF Handlers API. You can then strip the implicit xsd:string type off literals as they come into the system by overriding the HandleTriple(Triple t) method as desired.
Using the Handlers API as per RobV's answer:
class StripStringHandler : BaseRdfHandler, IWrappingRdfHandler
{
protected override bool HandleTripleInternal(Triple t)
{
if (t.Object is ILiteralNode)
{
var literal = t.Object as ILiteralNode;
if (literal.DataType != null && (literal.DataType.AbsoluteUri == "http://www.w3.org/2001/XMLSchema#string" || literal.DataType.AbsoluteUri == "http://www.w3.org/2001/XMLSchema#langString"))
{
var simpleLiteral = this.CreateLiteralNode(literal.Value, literal.Language);
t = new Triple(t.Subject, t.Predicate, simpleLiteral);
}
}
return this.handler.HandleTriple(t);
}
private IRdfHandler handler;
public StripStringHandler(IRdfHandler handler) : base(handler)
{
this.handler = handler;
}
public IEnumerable<IRdfHandler> InnerHandlers
{
get
{
return this.handler.AsEnumerable();
}
}
protected override void StartRdfInternal()
{
this.handler.StartRdf();
}
protected override void EndRdfInternal(bool ok)
{
this.handler.EndRdf(ok);
}
protected override bool HandleBaseUriInternal(Uri baseUri)
{
return this.handler.HandleBaseUri(baseUri);
}
protected override bool HandleNamespaceInternal(string prefix, Uri namespaceUri)
{
return this.handler.HandleNamespace(prefix, namespaceUri);
}
public override bool AcceptsAll
{
get
{
return this.handler.AcceptsAll;
}
}
}
Usage:
class Program
{
static void Main()
{
var graphA = Load("<http://example.com/subject> <http://example.com/predicate> \"object\".");
var graphB = Load("<http://example.com/subject> <http://example.com/predicate> \"object\"^^<http://www.w3.org/2001/XMLSchema#string>.");
var diff = graphA.Difference(graphB);
Debug.Assert(diff.AreEqual);
}
private static IGraph Load(string source)
{
var result = new Graph();
var graphHandler = new GraphHandler(result);
var strippingHandler = new StripStringHandler(graphHandler);
var parser = new TurtleParser();
using (var reader = new StringReader(source))
{
parser.Load(strippingHandler, reader);
}
return result;
}
}
Is there a way to quickly check the following logic in C#?
if (a)
{
}
if (b)
{
}
if (c)
{
}
else //none of the above, execute if all above conditions are false
{
/* do something only if !a && !b && !c */
}
This differs from using if-else in that a, b, and c can all be true at once, so I can't stack them that way.
I want to run the else block when a, b, and c are all false without writing if (!a && !b && !c). This is because the code can get quite messy when the if conditions become more complex. It requires rewriting a lot of code.
Is this possible?
Firstly, no, else blocks only respect the if clause immediately above them, so you'll need an alternative.
This option isn't especially "clean", but I'd do:
bool noneAreTrue = true;
if(a)
{
noneAreTrue = false;
}
if(b)
{
noneAreTrue = false;
}
if(c)
{
noneAreTrue = false;
}
if(noneAreTrue)
{
//execute if all above conditions are false
}
Also, if your conditions are really pretty big, I recommend the rule G28 (Encapsulate Conditionals) from the book Clean Code from Robert C. Martin.
It is pretty verbose, but can be easier to read in some instances:
public void YourMethod()
{
if(SomeComplexLogic())
{
}
if(SomeMoreLogic())
{
}
if(EvenMoreComplexLogic())
{
}
if(NoComplexLogicApply())
{
}
}
private bool SomeComplexLogic(){
return stuff;
}
private bool EvenMoreComplexLogic(){
return moreStuff;
}
private bool EvenMoreComplexLogic(){
return evenMoreStuff;
}
private bool NoComplexLogicApply(){
return SomeComplexLogic() && EvenMoreComplexLogic() && EvenMoreComplexLogic();
}
How about combine the concepts of Strategies and Specifications
var strategies = _availableStrategies.All(x => x.IsSatisfiedBy(value));
foreach (var strategy in strategies)
{
strategy.Execute(context);
}
if (!strategies.Any()) {
// run a different strategy
}
Rather than encapsulate some complex condition in a method that you will only ever call once or twice, I would just keep in a variable. This is also more readable than using some marker boolean as other answers suggest.
A contrived example,
bool isBlue = sky.Color == Colors.Blue;
bool containsOxygen = sky.Atoms.Contains("oxygen") && sky.Bonds.Type == Bond.Double;
bool canRain = sky.Abilities.Contains("rain");
if(isBlue)
{
}
if(containsOxygen)
{
}
if(canRain)
{
}
if(!isBlue && !containsOxygen && !canRain)
{
}
Now we have abstracted what might otherwise be complex conditions into readable English!
Given:
private void UpdataFieldItems(List<FieldItemBase> existingFieldItems, List<FieldItemBase> selectedFieldItems)
{
List<FieldItemBase> newFieldItemsSelected;
var fieldSelectionChanges = GetFieldSelectionChanges(out newFieldItemsSelected);//retuns a Flagged enum
if (Coding.EnumHas(fieldSelectionChanges, FieldSelectionChanges.AddedFieldItem))
{
StartEditMode();
SetColumnDescriptorsToAdd(newFieldItemsSelected);
UpdateItems(selectedFieldItems);
SetColumnsToShow();
CustomizeAlignmentAndCellFormatters(_tfaTableGrid.TableGrid);
if (_tfaTableGrid.TableGrid.ColumnDescriptors.Count() > 0)
{
SetAdditionalFirstGroupedColumn();
}
StopEditMode();
}
else if (Coding.EnumHas(fieldSelectionChanges, FieldSelectionChanges.Summary))
{
StartEditMode();
UpdateItems(fieldItems);
SetColumnsToShow();
if (_tfaTableGrid.TableGrid.ColumnDescriptors.Count() > 0)
{
SetAdditionalFirstGroupedColumn();
}
StopEditMode();
}
else if (Coding.EnumHas(fieldSelectionChanges, FieldSelectionChanges.Order) ||
Coding.EnumHas(fieldSelectionChanges, FieldSelectionChanges.AddedCustomFieldItem) ||
Coding.EnumHas(fieldSelectionChanges, FieldSelectionChanges.RemovedItem))
{
UpdateItems(fieldItems);
SetColumnsToShow();
}
Invalidate();
}
//Coding.cs
public static bool EnumHas(FieldSelectionChanges selectionChanges, FieldSelectionChanges valueToCheck)
{
return (selectionChanges & valueToCheck) == valueToCheck;
}
I am willing to refactor the above code.
There are two things that i don't like about the code above:
1) Writing same method calls in different cases, its not being possible to pull out the common method calls from these cases.
2) The readability of this code is very bad. It would be very confusing to understand and debug, if later needed.
Can someone suggest a design pattern for this code? or some way to improve upon the above two concerns?
Thanks for your interest.
Use extract method for body of each if statement
Create Dictionary> to choose appropriate action for fieldSelectionChanges. This is Strategy pattern
I would make the different conditions more expressive in terms of what it actually does to your application seeing you already use quite descriptive method names for the actions. Something like:
private void UpdataFieldItems(List<FieldItemBase> existingFieldItems, List<FieldItemBase> selectedFieldItems)
{
List<FieldItemBase> newFieldItemsSelected;
var fieldSelectionChanges = GetFieldSelectionChanges(out newFieldItemsSelected);//retuns a Flagged enum
if (IsValidChange(fieldSelectionChanges))
{
List<FieldItemBase> targetfields = null;
if (IsInEditMode(fieldSelectionChanges))
StartEditMode();
if (IsItemAdded(fieldSelectionChanges))
{
SetColumnDescriptorsToAdd(newFieldItemsSelected);
targetFields = selectedFieldItems;
}
else
targetFields = existingFieldItems;
UpdateItems(targetFields);
SetColumnsToShow();
if (IsItemAdded(fieldSelectionChanges))
CustomizeAlignmentAndCellFormatters(_tfaTableGrid.TableGrid);
if (IsInEditMode(fieldSelectionChanges))
{
if (_tfaTableGrid.TableGrid.ColumnDescriptors.Count() > 0)
SetAdditionalFirstGroupedColumn();
StopEditMode();
}
}
Invalidate();
}
private bool InEditMode(FlaggedEnum fieldSelectionChanges)
{
return Coding.EnumHas(fieldSelectionChanges, FieldSelectionChanges.Summary) || Coding.EnumHas(fieldSelectionChanges, FieldSelectionChanges.AddedFieldItem);
}
private bool IsItemAdded(FlaggedEnum fieldSelectionChanges)
{
Coding.EnumHas(Coding.EnumHas(fieldSelectionChanges, FieldSelectionChanges.AddedFieldItem);
}
private bool IsValidChange(FlaggedEnum fieldSelectionChanges)
{
return Coding.EnumHas(fieldSelectionChanges, FieldSelectionChanges.Order) ||
Coding.EnumHas(fieldSelectionChanges, FieldSelectionChanges.AddedCustomFieldItem) ||
Coding.EnumHas(fieldSelectionChanges, FieldSelectionChanges.RemovedItem) ||
Coding.EnumHas(fieldSelectionChanges, FieldSelectionChanges.Summary) ||
Coding.EnumHas(fieldSelectionChanges, FieldSelectionChanges.AddedFieldItem);
}
//Coding.cs
public static bool EnumHas(FieldSelectionChanges selectionChanges, FieldSelectionChanges valueToCheck)
{
return (selectionChanges & valueToCheck) == valueToCheck;
}
Well the part that is repetitive/somewhat ugly is the IF statements.
Suggest holding the result of those IF conditions in a boolean variable, and leverage that.
This code isnt complete, but you get the idea.
bool scenarioOne = Coding.EnumHas(fieldSelectionChanges, FieldSelectionChanges.AddedFieldItem);
bool scenarioTwo = Coding.EnumHas(fieldSelectionChanges, FieldSelectionChanges.Summary);
bool scenarioThree = Coding.EnumHas(fieldSelectionChanges, FieldSelectionChanges.Order) || Coding.EnumHas(fieldSelectionChanges, FieldSelectionChanges.AddedCustomFieldItem) || Coding.EnumHas(fieldSelectionChanges,FieldSelectionChanges.RemovedItem);
if (scenarioOne || scenarioTwo)
StartEditMode();
if (scenarioOne) {
SetColumnDescriptorsToAdd(newFieldItemsSelected);
UpdateItems(selectedFieldItems);
}
else if (scenarioTwo || scenarioThree) {
UpdateItems(fieldItems);
}
if (scenarioOne || scenarioTwo || scenarioThree)
SetColumnsToShow();
Obviously, pick better names for the variable.
Or better yet, seperate out into seperate methods.
I would suggest extracting the three blocks into separate, well-named, methods. This will improve the readability if the UpdateFieldItems method. Since the three blocks are totally different, there is imo no way of consolidating these further.
I have two functions whose underlying logic is the same but in one case it sets one property value on a class and in another case it sets a different one. How can I rewrite the following two functions to abstract away as much of the algorithm as possible so that I can make changes in logic in a single place?
SetBillingAddress
private void SetBillingAddress(OrderAddress newBillingAddress)
{
BasketHelper basketHelper = new BasketHelper(SiteConstants.BasketName);
OrderAddress oldBillingAddress = basketHelper.Basket.Addresses[basketHelper.BillingAddressID];
bool NewBillingAddressIsNotOldBillingAddress = ((oldBillingAddress == null) || (newBillingAddress.OrderAddressId != oldBillingAddress.OrderAddressId));
bool BillingAddressHasBeenPreviouslySet = (oldBillingAddress != null);
bool BillingAddressIsNotSameAsShippingAddress = (basketHelper.ShippingAddressID != basketHelper.BillingAddressID);
bool NewBillingAddressIsNotShippingAddress = (newBillingAddress.OrderAddressId != basketHelper.ShippingAddressID);
if (NewBillingAddressIsNotOldBillingAddress && BillingAddressHasBeenPreviouslySet && BillingAddressIsNotSameAsShippingAddress)
{
basketHelper.Basket.Addresses.Remove(oldBillingAddress);
}
if (NewBillingAddressIsNotOldBillingAddress && NewBillingAddressIsNotShippingAddress)
{
basketHelper.Basket.Addresses.Add(newBillingAddress);
}
basketHelper.BillingAddressID = newBillingAddress.OrderAddressId;
basketHelper.Basket.Save();
}
And here is the second one:
SetShippingAddress
private void SetShippingAddress(OrderAddress newShippingAddress)
{
BasketHelper basketHelper = new BasketHelper(SiteConstants.BasketName);
OrderAddress oldShippingAddress = basketHelper.Basket.Addresses[basketHelper.ShippingAddressID];
bool NewShippingAddressIsNotOldShippingAddress = ((oldShippingAddress == null) || (newShippingAddress.OrderAddressId != oldShippingAddress.OrderAddressId));
bool ShippingAddressHasBeenPreviouslySet = (oldShippingAddress != null);
bool ShippingAddressIsNotSameAsBillingAddress = (basketHelper.ShippingAddressID != basketHelper.BillingAddressID);
bool NewShippingAddressIsNotBillingAddress = (newShippingAddress.OrderAddressId != basketHelper.BillingAddressID);
if (NewShippingAddressIsNotOldShippingAddress && ShippingAddressHasBeenPreviouslySet && ShippingAddressIsNotSameAsBillingAddress)
{
basketHelper.Basket.Addresses.Remove(oldShippingAddress);
}
if (NewShippingAddressIsNotOldShippingAddress && NewShippingAddressIsNotBillingAddress)
{
basketHelper.Basket.Addresses.Add(newShippingAddress);
}
basketHelper.ShippingAddressID = newShippingAddress.OrderAddressId;
basketHelper.Basket.Save();
}
My initial thought was that if I could pass a class's property by refernce then I could rewrite the previous functions into something like
private void SetPurchaseOrderAddress(OrderAddress newAddress, ref String CurrentChangingAddressIDProperty)
and then call this function and pass in either basketHelper.BillingAddressID or basketHelper.ShippingAddressID as CurrentChangingAddressIDProperty but since I can't pass C# properties by reference I am not sure what to do with this code to be able to reuse the logic in both places.
Thanks for any insight you can give me.
You can pass lambda expressions:
private void SetPurchaseOrderAddress(OrderAddress newAddress, Func<string> addressIDGetter, Action<string> addressIDSetter)
Call it like this:
SetPurchaseOrderAddress(newAddress, () => basketHelper.BillingAddressID, a => basketHelper.BillingAddressID = a);
Send in a Func and an Action, one to get the property out, and another to set it again.
private void SetPurchaseOrderAddress(OrderAddress newAddress,
Func<BasketHelper, int> getAddId, Action<BasketHelper, int> setAddId) {}
Which would give something like:
SetPurchaseOrderAddress(address,
bh => bh.Basket.Addresses[bh.BillingAddressID],
(bh,s) => bh.BillingAddressID = s);
You can use delegates to create a method that takes some simple behavior (such as setting a value of the property) as an argument. In C# 3.0, you can easily create a delegate using lambda expressions and in C# 2.0 you can do similar thing using anonymous delegates.
In your example, you would write a method that takes Action<BasketHelper> delegate - the delegate allows you to specify some operation that should be done with the BasketHelper after you initialize it and before you save it:
private void SetBasketProperty(Action<BasketHelper> action) {
BasketHelper basketHelper = new BasketHelper(SiteConstants.BasketName);
// Code that is repeated for all similar methods
// Invoke the specific action
action(basketHelper);
basketHelper.Basket.Save();
}
To call this method in C# 3.0 you can write something like this:
SetBasketProperty(basketHelper => {
basketHelper.ShippingAddressID = newShippingAddress.OrderAddressId;
});
The lambda expression simply provides the code that you want to run before calling Save.
I have some C# code that walks XML schemata using the Xml.Schema classes from the .NET framework. The various simple type restrictions are abstracted in the framework as a whole bunch of classes derived from Xml.Schema.XmlSchemaFacet. Unless there is something I've missed, the only way to know which of the derived facet types a given facet is, is to speculatively cast it to one of them, catching the resultant InvalidCastOperation in case of failure. Doing this leaves me with a really ugly function like this:
private void NavigateFacet(XmlSchemaFacet facet)
{
try
{
handler.Length((XmlSchemaLengthFacet)facet);
}
catch(InvalidCastException)
{
try
{
handler.MinLength((XmlSchemaMinLengthFacet)facet);
}
catch(InvalidCastException)
{
try
{
handler.MaxLength((XmlSchemaMaxLengthFacet)facet);
}
catch(InvalidCastException)
{
...
}
}
}
}
I assume there must be more elegant ways to do this; either using some property I've missed from the .NET framework, or with some clever piece of OO trickery. Can anyone enlighten me?
Because I prefer debugging data to debugging code, I'd do it like this, especially if the code had to handle all of the XmlSchemaFacet subclasses:
Dictionary<Type, Action<XmlSchemaFacet>> HandlerMap =
new Dictionary<Type, Action<XmlSchemaFacet>>
{
{typeof(XmlSchemaLengthFacet), handler.Length},
{typeof(XmlSchemaMinLengthFacet), handler.MinLength},
{typeof(XmlSchemaMaxLengthFacet), handler.MaxLength}
};
HandlerMap[facet.GetType()](facet);
This'll throw a KeyNotFoundException if facet isn't of a known type. Note that all of the handler methods will have to cast their argument from XmlSchemaFacet, so you're probably not saving on total lines of code, but you're definitely saving on the number of paths through your code.
There also comes a point where (assuming that the map is pre-built) mapping types to methods with a dictionary will be faster than traversing a linear list of types, which is essentially what using a bunch of if blocks gets you.
You can try using the as keyword. Some others have recommended using the is keyword instead. I found this to be a great explanation of why as is better.
Some sample code:
private void NavigateFacet(XmlSchemaFacet facet)
{
XmlSchemaLengthFacet lengthFacet = facet as XmlSchemaLengthFacet;
if (lengthFacet != null)
{
handler.Length(lengthFacet);
}
else
{
// Re-try with XmlSchemaMinLengthFacet, etc.
}
}
private void NavigateFacet(XmlSchemaFacet facet)
{
if (facet is XmlSchemaLengthFacet)
{
handler.Length((XmlSchemaLengthFacet)facet);
}
else if (facet is XmlSchemaMinLengthFacet)
{
handler.MinLength((XmlSchemaMinLengthFacet)facet);
}
else if (facet is XmlSchemaMaxLengthFacet)
{
handler.MinLength((XmlSchemaMaxLengthFacet)facet);
}
// etc.
}
Update: I decided to benchmark the different methods discussed here (is vs. as). Here is the code that I used:
object c1 = new Class1();
int trials = 10000000;
Class1 tester;
Stopwatch watch = Stopwatch.StartNew();
for (int i = 0; i < trials; i++)
{
if (c1 is Class1)
{
tester = (Class1)c1;
}
}
watch.Stop();
MessageBox.Show(watch.ElapsedMilliseconds.ToString()); // ~104 ms
watch.Reset();
watch.Start();
for (int i = 0; i < trials; i++)
{
tester = c1 as Class1;
if (tester != null)
{
//
}
}
watch.Stop();
MessageBox.Show(watch.ElapsedMilliseconds.ToString()); // ~86 ms
watch.Reset();
watch.Start();
for (int i = 0; i < trials; i++)
{
if (c1 is Class1)
{
//
}
}
watch.Stop();
MessageBox.Show(watch.ElapsedMilliseconds.ToString()); // ~74 ms
watch.Reset();
watch.Start();
for (int i = 0; i < trials; i++)
{
//
}
watch.Stop();
MessageBox.Show(watch.ElapsedMilliseconds.ToString()); // ~50 ms
As expected, using the as keyword and then checking for null is faster than using the is keyword and casting (36 ms vs. 54 ms, subtracting the cost of the loop itself).
However, using the is keyword and then not casting is faster still (24ms), which means that finding the correct type with a series of is checks and then only casting once when the correct type is identified could actually be faster (depending upon the number of different type checks that have to be done in this method before the correct type is identified).
The deeper point, however, is that the number of trials in this test is 10 million, which means it really doesn't make much difference which method you use. Using is and casting takes 0.0000054 milliseconds, while using as and checking for null takes 0.0000036 milliseconds (on my ancient notebook).
You could try using the as keyword - if the cast fails, you get a null instead of an exception.
private void NavigateFacet(XmlSchemaFacet facet)
{
var length = facet as XmlSchemaLengthFacet;
if (length != null)
{
handler.Length(length);
return;
}
var minlength = facet as XmlSchemaMinLengthFacet;
if (minlength != null)
{
handler.MinLength(minlength);
return;
}
var maxlength = facet as XmlSchemaMaxLengthFacet;
if (maxlength != null)
{
handler.MaxLength(maxlength);
return;
}
...
}
If you had control over the classes, I'd suggest using a variant of the Visitor pattern (aka Double Despatch to recover the type information more cleanly, but since you don't, this is one relatively simple approach.
Update: Using the variable to store the result of the as cast avoids the need to go through the type checking logic twice.
Update 2: When C# 4 becomes available, you'll be able to use dynamic to do the dispatching for you:
public class HandlerDemo
{
public void Handle(XmlSchemaLengthFacet facet) { ... }
public void Handle(XmlSchemaMinLengthFacet facet) { ... }
public void Handle(XmlSchemaMaxLengthFacet facet) { ... }
...
}
private void NavigateFacet(XmlSchemaFacet facet)
{
dynamic handler = new HandlerDemo();
handler.Handle(facet);
}
This will work because method dispatch on dynamic objects uses the same rules as normal method overriding, but evaluated at run-time instead of compile-time.
Under the hood, the Dynamic Language Runtime (DLR) will be doing much the same kind of trick as the code shown in this (and other answers), but with the addition of caching for performance.
This is a proper way to do this without any try-catches.
if (facet is XmlSchemaLengthFacet)
{
handler.Length((XmlSchemaLengthFacet)facet);
}
else if (facet is XmlSchemaMinLengthFacet)
{
handler.MinLength((XmlSchemaMinLengthFacet)facet);
}
else if (facet is XmlSchemaMaxLengthFacet)
{
handler.MaxLength((XmlSchemaMaxLengthFacet)facet);
}
else
{
//Handle Error
}
use "is" to determine whether an object is of a given type
use "as" for type conversion, it is faster than normal casting and won't throw exceptions, it reuturns null on error instead
You can do it like this:
private void NavigateFacet(XmlSchemaFacet facet)
{
if (facet is XmlSchemaLengthFacet)
{
handler.Length(facet as XmlSchemaLengthFacet);
}
else if (facet is XmlSchemaMinLengthFacet)
{
handler.MinLength(facet as XmlSchemaMinLengthFacet);
}
else if (facet is XmlSchemaMaxLengthFacet)
{
handler.MaxLength(facet as XmlSchemaMaxLengthFacet);
}
}