gpt4 book ai didi

c# - 使用泛型和后期绑定(bind)的反射。如何在运行时转换?

转载 作者:太空宇宙 更新时间:2023-11-03 13:09:22 29 4
gpt4 key购买 nike

我正在尝试在 C# 中使用泛型和反射来构建一个可以处理多个类的方法。我使用具有一堆类的第 3 方 DLL,在这些类上,有一个我调用的方法。它们都返回不同的返回类型,但是一旦我取回对象(在我下面的示例中,这将是 AreaA 和 AreaB),我就会执行相同的处理。

基本上,我想开发一种方法,将类名和预期的返回类型作为通用变量,然后调用作为此方法的参数提供的正确方法 (methodName) .

下面的程序编译正常并且运行没有错误,但问题是“area”变量的预期类型。在下面的语句中,第一行被类型转换为 (TArea),如果我将鼠标悬停在它上面,在 Visual Studio 中,智能感知会显示属性“名称”,但键入 area.name 不会给我值(value)。我必须输入 ((AreaA)area).name

问题是“AreaA”类型在运行时可能是另一种类型。在这个例子中,'AreaB' 这样我就可以对 Actor 进行硬编码。

如何才能将“区域”变量转换为适当的类型,从而允许我查看第 3 方类的公共(public)方法/属性?

注意:在我的示例中,所有内容都在同一个类中,但实际上 ServiceA、ServiceB、AreaA 和 AreaB 的定义将在第 3 方 DLL 中。

一如既往,提前致谢!

图 1 - 如果转换为“AreaA”,“area”变量只能获得“name”属性

area = (TArea)dfpMethod.Invoke(instance, new object[] { "Area123" });
AreaA areaa = (AreaA)dfpMethod.Invoke(instance, new object[] { "Area123" });

图 2. - 完整程序

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using System.IO;


namespace ReflectionExample
{
class Sample
{
class ServiceA
{
public int size {get; set;}
public string name {get; set;}

public ServiceA()
{
name = "TestA";
size = 100;
}
public AreaA doWork(string name)
{
return new AreaA(name);

}
}

class AreaA
{
public string name { get; set;}
public AreaA(string name)
{
this.name = name;
}
public AreaA()
{

}

}

class ServiceB
{
public int size { get; set; }
public string name { get; set; }

public ServiceB()
{
name = "TestB";
size = 50;
}
public AreaB doWork(string name)
{
return new AreaB(name);
}

}

class AreaB
{
public string name { get; set; }
public AreaB(string name)
{
this.name = name;
}
public AreaB()
{

}
}

static void Main(string[] args)
{
runService<ServiceA, AreaA>("doWork");
}

private static void runService<TService, TArea>(string methodName)
where TService : class, new()
where TArea : class, new()
{
//Compile time processing
Type areaType = typeof(TArea);
Type serviceType = typeof(TService);


//Print the full assembly name and qualified assembly name
Console.WriteLine("AreaType--Full assembly name:\t {0}.", areaType.Assembly.FullName.ToString()); // Print the full assembly name.
Console.WriteLine("AreaType--Qualified assembly name:\t {0}.", areaType.AssemblyQualifiedName.ToString()); // Print the qualified assembly name.
Console.WriteLine("ServiceType--Full assembly name:\t {0}.", serviceType.Assembly.FullName.ToString()); // Print the full assembly name.
Console.WriteLine("ServiceType--Qualified assembly name:\t {0}.", serviceType.AssemblyQualifiedName.ToString()); // Print the qualified assembly name.

//This is done because in my code, the assembly doesn't reside in the executiy assembly, it is only setup as a reference
var assembly = Assembly.Load(serviceType.Assembly.FullName);

//Initialize the generic area
TArea area = default(TArea);
//Get an instance of the service so I can invoke the method later on
var instance = Activator.CreateInstance(serviceType);

//Get the methodInfo for the methodName supplied to the runService method
MethodInfo dfpMethod = serviceType.GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance);

//area is type casted to (TArea), the intellisense shows the property 'name', but typing area.name doesn't give me the value
//I have to type ((AreaA)area).name. Problem is the type 'AreaA' could be another type. In this example, 'AreaB'
area = (TArea)dfpMethod.Invoke(instance, new object[] { "Area123" });
AreaA areaa = (AreaA)dfpMethod.Invoke(instance, new object[] { "Area123" });
Console.WriteLine();

}

}
}

最佳答案

您的错误来源是您将所有返回值强制转换为 TArea 类型的语句:

TArea area = (TArea)dfpMethod.Invoke(instance, new object[] { "Area123" });

根据您的通用规范,TArea 类型唯一向您 promise 的是它是一个类。因此,除了“对象”类型的成员之外,TArea 不会让您访问任何内容。

相反,取消 TArea 通用参数以支持使用“动态”关键字:

var area = (dynamic)dfpMethod.Invoke(instance, new object[] { "Area123" });
return area.name; // no error

请注意,只有在第三方库中定义了实际类型 AreaA 和 AreaB(如您所说)并且您无法修改它们时,这才相关。如果你不能修改类,你就不能引入接口(interface)(这是你真正需要的)。如果不能引入接口(interface),但所有类型都公开相同的签名,则可以假定存在使用动态类型的相关成员。

如果您需要使用 AreaA/AreaB 做很多工作并且您不希望所有动态操作的性能开销,请定义您自己的通用类来公开您需要的所有签名:

public class MyGenericArea 
{
public MyGenericArea(string name)
{
this.Name = name;
}
public string Name {get; set;}
}

然后使用动态转换填充类并返回该类类型:

 var area = (dynamic)dfpMethod.Invoke(instance, new object[] { "Area123" });
return new MyGenericArea(area.name);

关于c# - 使用泛型和后期绑定(bind)的反射。如何在运行时转换?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29656878/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com