Skip to content

Instantly share code, notes, and snippets.

@CurtHagenlocher
Created February 26, 2012 15:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CurtHagenlocher/1917432 to your computer and use it in GitHub Desktop.
Save CurtHagenlocher/1917432 to your computer and use it in GitHub Desktop.
StringFormat.cs
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