C#:找出代码中算术运算的结果类型(例如int+;double=double)
我想用一个函数来确定算术运算的结果类型,比如加法:C#:找出代码中算术运算的结果类型(例如int+;double=double),c#,reflection,C#,Reflection,我想用一个函数来确定算术运算的结果类型,比如加法: Type TypeOfAddition(Type leftType, Type rightType) { // ??? } Type TypeOfMultiplication(Type leftType, Type rightType) { // ??? } // ... same for subtraction and division 这些功能的预期结果可能是明确的;本质上,我的目标是在运行时执行与VisualStudio在执
Type TypeOfAddition(Type leftType, Type rightType)
{
// ???
}
Type TypeOfMultiplication(Type leftType, Type rightType)
{
// ???
}
// ... same for subtraction and division
这些功能的预期结果可能是明确的;本质上,我的目标是在运行时执行与VisualStudio在执行算术运算时将类型推断为“var”类型变量相同的操作
比如说,
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)提出一个智能解决方案,但这似乎相对。。不合法
有没有更聪明/更容易/更好的解决方案?
请注意,我只想得到这样一个运算结果的理论类型,而不需要显式地执行算术运算
谢谢 也许你可以在你的方法中尝试泛型。差不多
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);
}
添加类型(T leftNum,T2 rightNum){
var result=leftNum+rightNum;
返回类型(结果);
}
乘法类型(T leftNum,T2 rightNum){
var result=leftNum*rightNum;
返回类型(结果);
}
它当然不漂亮,速度也不快,但它似乎与我运行过的基本测试一起工作 请注意,您需要引用
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);
}
TypeOfAddition()类型
{
对象GetDefault()
{
if(类型(T).IsValueType)
{
返回默认值(T);
}
if(typeof(T)=typeof(string))
{
返回字符串。空;
}
return(T)FormatterServices.GetUninitializedObject(typeof(T));
}
var binder=Microsoft.CSharp.RuntimeBinder.binder.BinaryOperation(
CSharpBinderFlags,无,
ExpressionType.Add,
无效的
新CSharpArgumentInfo[]{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None,null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None,null)
}
);
var left=表达式参数(typeof(TLeft));
var right=Expression.Parameter(typeof(TRight));
var func=Expression.Lambda(
表达式.动态(活页夹、类型(对象)、左、右),
新[]{左,右}
).Compile();
返回函数
.DynamicInvoke(GetDefault(),GetDefault())
?.GetType()?(对象)的类型;
}
示例输出:
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
公共类MyClass
{
公共静态字符串运算符+(MyClass左,双右)
{
返回“”;
}
}
TypeOfAddition().Dump();//系统字符串
TypeOfAddition().Dump();//系统,双人
TypeOfAddition().Dump();//系统,双人
TypeOfAddition().Dump();//系统字符串
这使用了Jeroen在注释(RuntimeBinder)中提到的内容来创建一个附加的binder。然后,它构建一个动态表达式树来添加TLeft
和TRight
的默认值。我必须添加一个名为GetDefault
的小函数来将string
解析为空字符串,因为我假设您在尝试添加“+0
而不是null
时希望看到string
。如果确实希望看到空值,只需将GetDefault
调用替换为default(TLeft)
和default(TRight)
它不调用构造函数(由于使用)包含字符串的特殊情况
可能有很多可能的改进,我洗耳恭听 使用Roslyn,我现在得出了以下结论。与我测试的结果相去甚远,它似乎工作得很好——让我知道你的想法 我所看到的缺点(在我的情况下没有什么大问题)是
- 有点慢,至少第一次打电话时是这样
- 必须显式引用程序集。不确定这是否会触发将它们重新加载到AppDomain/context 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; }
有帮助吗?它显示了哪些类型将被“强制”到其他类型(这基本上是您所需要的)。为什么您一开始就想这样做?这可能是一个@mjwills谢谢-如果我不得不采用硬编码的方式,它确实有很大帮助!这一点都不容易。具体来说,重载解析是语言/编译器逻辑的一部分,也是我所知道的唯一一种运行时表示形式,存在于
中。利用这一点并不容易,因为其中大部分都是故意不记录的(您应该只使用Microsoft.CSharp.RuntimeBinder
而不必担心)。C#关于运算符的重载解析规则是,因此,除非您的可能性受到严格限制,否则我建议不要重新发明轮子。可能必须启动Roslyn并要求它编译一个小样本(使用动态
默认值
)然后询问表达式的结果类型。泛型和数学运算符根本不混合。您将无法编译包含
的代码。即使如此,我打赌提供方法类型变量并获取动态类型结果的类型要比直接使用类型更容易。也许您可以尝试使用集合封装基本类型,如列表,然后只使用它的第一个索引。不过,这会在运行时创建值,这对于+
和int
很好,但是对于double
(默认值为MyClass
,而且,我们不希望调用构造函数,包括可能的副作用)。感谢您的建议/工作,谢谢你!对于自定义类型,这似乎不适用于我,null
Type t1 = await GetOperationResultTypeAsync(typeof(MyClass), typeof(double), "+"); Type t2 = await GetOperationResultTypeAsync(typeof(int), typeof(int), "+"); Type t3 = await GetOperationResultTypeAsync(typeof(int), typeof(double), "+");