Created
February 26, 2012 15:49
-
-
Save CurtHagenlocher/1917432 to your computer and use it in GitHub Desktop.
StringFormat.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class StringFormat : IDynamicMetaObjectProvider | |
{ | |
/// <summary> | |
/// Usage: StringFormat.Format(formatString, key1: value1, key2: value2) | |
/// or StringFormat.Format(formatProvider, formatString, key1: value1, key2: value2) | |
/// </summary> | |
public static readonly dynamic Format = new StringFormat(); | |
private StringFormat() | |
{ | |
} | |
DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter) | |
{ | |
return new StringFormatMetaObject(parameter, this); | |
} | |
class StringFormatMetaObject : DynamicMetaObject | |
{ | |
private static readonly MethodInfo _stringFormatWithProvider = typeof(String).GetMethod("Format", new[] { typeof(IFormatProvider), typeof(string), typeof(object[]) }); | |
private static readonly MethodInfo[] _stringFormatMethod = | |
{ | |
typeof(String).GetMethod("Format", new[] { typeof(string), typeof(object) }), | |
typeof(String).GetMethod("Format", new[] { typeof(string), typeof(object), typeof(object) }), | |
typeof(String).GetMethod("Format", new[] { typeof(string), typeof(object), typeof(object), typeof(object) }) | |
}; | |
private static readonly MethodInfo _stringFormatVarargs = typeof(String).GetMethod("Format", new[] { typeof(string), typeof(object[]) }); | |
private static readonly ConstructorInfo _argumentExceptionConstructor = typeof(ArgumentException).GetConstructor(new[] { typeof(string), typeof(string) }); | |
private static readonly Expression _stringFormatConstant = Expression.Constant("stringFormat"); | |
private static readonly Expression _trueExpression = Expression.Constant(true); | |
private static readonly Expression _nullExpression = Expression.Constant(null, typeof(object)); | |
private static readonly Expression _exceptionExpression1 = Expression.Throw( | |
Expression.New( | |
_argumentExceptionConstructor, | |
Expression.Constant("Expected one or two non-named arguments and zero or more named arguments"), | |
_stringFormatConstant)); | |
private static readonly Expression _exceptionExpression2 = Expression.Throw( | |
Expression.New( | |
_argumentExceptionConstructor, | |
Expression.Constant("First argument must be either a format string or an IFormatProvider"), | |
_stringFormatConstant)); | |
private static readonly Expression _exceptionExpression3 = Expression.Throw( | |
Expression.New( | |
_argumentExceptionConstructor, | |
Expression.Constant("Second argument must be a format string"), | |
_stringFormatConstant)); | |
internal StringFormatMetaObject(Expression parameter, StringFormat builder) | |
: base(parameter, BindingRestrictions.Empty, builder) | |
{ | |
} | |
private static string ConvertFormatString(string formatString, CallInfo callInfo) | |
{ | |
for (int i = 0; i < callInfo.ArgumentNames.Count; i++) | |
{ | |
formatString = formatString.Replace("{" + callInfo.ArgumentNames[i] + "}", "{" + i.ToString(CultureInfo.InvariantCulture) + "}"); | |
} | |
return formatString; | |
} | |
public override DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObject[] args) | |
{ | |
int positionalArgs = binder.CallInfo.ArgumentCount - binder.CallInfo.ArgumentNames.Count; | |
if (positionalArgs == 0 || positionalArgs > 2 || binder.CallInfo.ArgumentCount < 2) | |
{ | |
return new DynamicMetaObject( | |
_exceptionExpression1, | |
BindingRestrictions.GetInstanceRestriction(this.Expression, this.Value)); | |
} | |
if (positionalArgs == 2 && !typeof(IFormatProvider).IsAssignableFrom(args[0].LimitType)) | |
{ | |
return new DynamicMetaObject( | |
_exceptionExpression2, | |
BindingRestrictions.GetInstanceRestriction(this.Expression, this.Value)); | |
} | |
if (args[positionalArgs - 1].LimitType != typeof(string)) | |
{ | |
return new DynamicMetaObject( | |
_exceptionExpression3, | |
BindingRestrictions.GetInstanceRestriction(this.Expression, this.Value)); | |
} | |
Expression stringFormatExpression = Expression.Constant(ConvertFormatString((string)args[positionalArgs - 1].Value, binder.CallInfo)); | |
Expression result; | |
if (positionalArgs == 2 || args.Length > 4) | |
{ | |
var exprArray = new Expression[args.Length - positionalArgs]; | |
for (int i = 0; i < exprArray.Length; i++) | |
{ | |
exprArray[i] = args[i + positionalArgs].Expression; | |
} | |
var paramArgs = Expression.NewArrayInit(typeof(object), exprArray); | |
result = (positionalArgs == 2) ? | |
Expression.Call(_stringFormatWithProvider, args[0].Expression, stringFormatExpression, paramArgs) : | |
Expression.Call(_stringFormatVarargs, stringFormatExpression, paramArgs); | |
} | |
else | |
{ | |
var exprArray = new Expression[args.Length]; | |
exprArray[0] = stringFormatExpression; | |
for (int i = 1; i < exprArray.Length; i++) | |
{ | |
exprArray[i] = args[i].Expression; | |
} | |
result = Expression.Call(_stringFormatMethod[args.Length - 2], exprArray); | |
} | |
return new DynamicMetaObject( | |
result, | |
BindingRestrictions.GetInstanceRestriction(args[positionalArgs - 1].Expression, args[positionalArgs - 1].Value)); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment