Skip to content

Instantly share code, notes, and snippets.

@davidwhitney
Created January 11, 2013 17:14
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davidwhitney/4512395 to your computer and use it in GitHub Desktop.
Save davidwhitney/4512395 to your computer and use it in GitHub Desktop.
A simpler way to parse strings in C# Parsing strings in C# can be verbose and horrible. The last thing you want to do when solving some real problem, is end up with a load of code extracting values from strings and marshalling them between data types. It’s silly, so lets not do it! Instead, I offer you an extension method and some tests. Do with…
public class StringParsingNeedsToBeSimpler
{
public void HereAreSomeExamples()
{
//You know what sucks?
string value = "123";
int outt;
if(!int.TryParse(value, out outt))
{
outt = 12345; // my default!
}
// That's ugly and verbose.
// So lets try these instead!
var betterWay = value.Parse(onFailure: () => 12345);
var evenBetter = value.Parse(() => 12345);
var youCanEvenDo = value.Parse<double>();
var andCheckThisOut = "01/01/2012".Parse<DateTime>();
var andThis = "01/01/2012".Parse<DateTime>(()=>DateTime.Now);
// Now isn't that a nice concise way to parse!
// Tests included below.
}
}
using System;
public static class TypeParsingExtensions
{
public static object Parse(this string source, Type ttype, Func<object> onFailure = null)
{
object parsed;
try
{
parsed = Convert.ChangeType(source, ttype);
}
catch (Exception)
{
if (onFailure == null)
{
throw;
}
return onFailure();
}
return parsed;
}
public static TType Parse<TType>(this string source, Func<TType> onFailure = null)
{
return (TType)source.Parse(typeof(TType), onFailure != null ? (Func<object>)(() => onFailure()) : null);
}
}
using System;
using System.Collections.Generic;
using NUnit.Framework;
[TestFixture]
public class TypeParsingExtensionsTests
{
private readonly List<TestCase> _testCases;
public TypeParsingExtensionsTests()
{
_testCases = new List<TestCase>
{
TestCase.For(validExample: 123, @default: 987),
TestCase.For(validExample: 123.54m, @default: 987.25m),
TestCase.For<float>(validExample: 123456800, @default: 987654321),
TestCase.For<byte>(validExample: 1, @default: 2),
TestCase.For(validExample: 123.456, @default: 543.321),
};
}
// Tests use Parse(type) rather than Parse<type>() to keep them DRY (if slightly less clear).
[Test]
public void Parse_PassedValid_Parses()
{
foreach (var testCase in _testCases)
{
var output = testCase.Valid.Parse(testCase.TestType);
Assert.That(output, Is.EqualTo(testCase.Expectation));
}
}
[Test]
public void Parse_PassedInvalid_ThrowsFormatException()
{
foreach (var testCase in _testCases)
{
Assert.Throws<FormatException>(() => testCase.Invalid.Parse(testCase.TestType));
}
}
[Test]
public void Parse_PassedInvalidWithDefault_ReturnsDefault()
{
foreach (var testCase in _testCases)
{
var @case = testCase;
var output = testCase.Invalid.Parse(testCase.TestType, () => @case.Default);
Assert.That(output, Is.EqualTo(testCase.Default));
}
}
private class TestCase
{
public string Valid { get; private set; }
public string Invalid { get; private set; }
public object Expectation { get; private set; }
public object Default { get; private set; }
public Type TestType { get; private set; }
public static TestCase For<TType>(TType validExample, TType @default)
{
return new TestCase(validExample.ToString()) { Expectation = validExample, Default = @default, TestType = typeof(TType) };
}
private TestCase(string valid)
{
Valid = valid;
Invalid = "Invalid";
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment