我正在尝试提出一个函数来确定算术运算的结果类型,比如添加的情况:
Type TypeOfAddition(Type leftType, Type rightType)
{
// ???
}
Type TypeOfMultiplication(Type leftType, Type rightType)
{
// ???
}
// ... same for subtraction and division
这些功能的期望结果可能很清楚;从本质上讲,我的目标是在执行算术运算时,在将类型推断为“var”类型变量时Visual Studio所做的相同(在运行时)。
例如,
public class MyClass
{
public static string operator +(MyClass left, double right)
{
// ...
}
}
TypeOfAddition(typeof(int), typeof(double)); // Should return typeof(double)
TypeOfAddition(typeof(string), typeof(int)); // Should return typeof(string)
TypeOfAddition(typeof(MyClass), typeof(double)); // Should return typeof(string)
我的基本想法是概念性的实施
Type TypeOfAddition(Type leftType, Type rightType)
{
return leftType.GetMethods().Single(x =>
x.Name == "op_Addition" &&
x.GetParamters().Count == 2 &&
x.GetParameters().Last().ParameterType == rightType);
}
但
A)这对基本类型(如int,double等)不起作用,它们似乎没有明确定义运算符重载,并且
B)上述linq条款尚未捕获所有案例(例如继承)
我可以对基本类型进行硬编码,并试图为B)提供一个智能解决方案,但这似乎相对......不优雅。
有没有更聪明/更容易/更好的解决方案来解决这个问题? 请注意,我只想获得这种操作结果的理论类型,而不是显式执行算术运算。
谢谢!
答案 0 :(得分:3)
它当然不漂亮,绝对不是很快,但它似乎适用于我经历过的基本测试。
请注意,您需要引用Microsoft.CSharp.dll
。
Type TypeOfAddition<TLeft, TRight>()
{
object GetDefault<T>()
{
if (typeof(T).IsValueType)
{
return default(T);
}
if (typeof(T) == typeof(string))
{
return string.Empty;
}
return (T)FormatterServices.GetUninitializedObject(typeof(T));
}
var binder = Microsoft.CSharp.RuntimeBinder.Binder.BinaryOperation(
CSharpBinderFlags.None,
ExpressionType.Add,
null,
new CSharpArgumentInfo[] {
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
}
);
var left = Expression.Parameter(typeof(TLeft));
var right = Expression.Parameter(typeof(TRight));
var func = Expression.Lambda(
Expression.Dynamic(binder, typeof(object), left, right),
new[] { left, right }
).Compile();
return func
.DynamicInvoke(GetDefault<TLeft>(), GetDefault<TRight>())
?.GetType() ?? typeof(object);
}
示例输出:
public class MyClass
{
public static string operator +(MyClass left, double right)
{
return "";
}
}
TypeOfAddition<string, int>().Dump(); // System.String
TypeOfAddition<int, double>().Dump(); // System.Double
TypeOfAddition<float, double>().Dump(); // System.Double
TypeOfAddition<MyClass, double>().Dump(); // System.String
这使用Jeroen在评论(RuntimeBinder)中提到的内容来创建附加活页夹。然后,它会构建一个动态表达式树,以添加TLeft
和TRight
的默认值。我必须添加一个名为GetDefault
的小函数来将string
解析为空字符串,因为我假设您在尝试添加string
而不是"" + 0
时希望看到null
GetDefault
。如果您执行想要查看空值,只需将default(TLeft)
来电替换为default(TRight)
和 $query = $analytics->data_ga->get('ga:' . $ga_profile_id, $start_date, $end_date, 'ga:sessions', array('max-results' => 10000));
$query = $analytics->data_ga->get('ga:' . $ga_profile_id, $start_date, $end_date, 'ga:visitBounceRate', array('max-results' => 1000, 'dimensions' => 'ga:channelGrouping',));
if ($query['totalResults'] != 0 && isset($query['rows'])) {
$bounce_rate_total = round($query['totalsForAllResults']['ga:visitBounceRate'],2);
print 'Bounce Rate Total: '.$bounce_rate_total.'<br/>';
$bounce_rate_organic = round($query['rows'][2][1],2);
print 'Bounce Rate organic: '.$bounce_rate_organic.'<br/>';
$bounce_rate_ppc = round($query['rows'][3][1],2);
print 'Bounce Rate PPC: '.$bounce_rate_ppc.'<br/>';
$bounce_rate_social = round($query['rows'][5][1],2);
print 'Bounce Rate Social: '.$bounce_rate_social.'<br/>';
}
。
它不调用构造函数(由于使用GetUninitializedObject
)包含字符串的特殊情况。
可能有很多可能的改进,我很满意。
答案 1 :(得分:0)
使用Roslyn,我现在想出了以下内容。到目前为止,我测试的似乎工作正常 - 让我知道你的想法。
从我看到的内容(虽然我的情况下没有重大问题)
显然需要为我的应用程序使用/部署很多Roslyn程序集
static async Task<Type> GetOperationResultTypeAsync(Type left, Type right, string operatorSymbol)
{
// Reference all assemblies that are loaded in the current AppDomain (plugins?)
var options = ScriptOptions.Default.AddReferences(AppDomain.CurrentDomain.GetAssemblies());
var script = CSharpScript.Create($"var instance = default({left.FullName}) {operatorSymbol} default({right.FullName});", options: options);
var compilation = script.GetCompilation();
var syntaxTree = compilation.SyntaxTrees.Single();
var semanticModel = compilation.GetSemanticModel(syntaxTree);
var variableDeclaration = (await syntaxTree.GetRootAsync())
.DescendantNodes()
.OfType<VariableDeclarationSyntax>()
.Single();
var symbolInfo = semanticModel.GetSymbolInfo(variableDeclaration.Type);
var typeSymbol = (ITypeSymbol)symbolInfo.Symbol; // will be null on error (eg operation not possible/defined/allowed)
if (typeSymbol == null)
return null;
var symbolDisplayFormat = new SymbolDisplayFormat(typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces);
string fullyQualifiedName = typeSymbol.ToDisplayString(symbolDisplayFormat);
Type type = Type.GetType(fullyQualifiedName, throwOnError: true);
return type;
}
用法只是
Type t1 = await GetOperationResultTypeAsync(typeof(MyClass), typeof(double), "+");
Type t2 = await GetOperationResultTypeAsync(typeof(int), typeof(int), "+");
Type t3 = await GetOperationResultTypeAsync(typeof(int), typeof(double), "+");
答案 2 :(得分:-1)
也许你可以在你的方法中尝试泛型。像
这样的东西Type TypeOfAddition<T, T2>(T leftNum, T2 rightNum){
var result = leftNum + rightNum;
return typeof(result);
}
Type TypeOfMultiplication<T, T2>(T leftNum, T2 rightNum){
var result = leftNum * rightNum;
return typeof(result);
}