Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/306.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用泛型和后期绑定的反射。如何在运行时播放?_C#_Generics_System.reflection_Late Binding - Fatal编程技术网

C# 使用泛型和后期绑定的反射。如何在运行时播放?

C# 使用泛型和后期绑定的反射。如何在运行时播放?,c#,generics,system.reflection,late-binding,C#,Generics,System.reflection,Late Binding,我试图在c#中使用带有反射的泛型来构建一个可以处理多个类的方法。我使用一个第三方DLL,它有一堆类,在这些类上,我调用了一个方法。它们都返回不同的返回类型,但一旦我得到对象,我会执行相同的处理(在下面的示例中,这将是区域A和区域B) 基本上,我想开发一个方法,将类名和预期返回类型作为泛型变量,然后调用正确的方法(methodName),该方法作为该方法的参数提供 下面的程序编译良好,运行无误,但问题是“area”变量的预期类型。在下面的语句中,第一行是类型casted to(TArea),如果我

我试图在c#中使用带有反射的泛型来构建一个可以处理多个类的方法。我使用一个第三方DLL,它有一堆类,在这些类上,我调用了一个方法。它们都返回不同的返回类型,但一旦我得到对象,我会执行相同的处理(在下面的示例中,这将是区域A和区域B

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

下面的程序编译良好,运行无误,但问题是“area”变量的预期类型。在下面的语句中,第一行是类型casted to(TArea),如果我在Visual Studio中将鼠标悬停在它上面,intellisense会显示属性“name”,但键入区域。name不会给我该值。我必须键入((area)area).name

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

如何将“area”变量强制转换为适当的类型,以便查看第三方类的公共方法/属性

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

一如既往,提前感谢

图1-“area”变量只能在强制转换为“AreaA”时获得“name”属性。

图2-完成课程

使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
使用System.Threading.Tasks;
运用系统反思;
使用System.IO;
名称空间反射示例
{
类样本
{
A类服务
{
公共整数大小{get;set;}
公共字符串名称{get;set;}
公共服务A()
{
name=“TestA”;
尺寸=100;
}
公共区域A doWork(字符串名称)
{
返回新区域A(名称);
}
}   
A类区域
{
公共字符串名称{get;set;}
公共区域A(字符串名称)
{
this.name=名称;
}
公共区域a()
{
}
}
B类服务
{
公共整数大小{get;set;}
公共字符串名称{get;set;}
公共服务B()
{
name=“TestB”;
尺寸=50;
}
公共区域B doWork(字符串名称)
{
返回新区域B(名称);
}
}
B类地区
{
公共字符串名称{get;set;}
公共区域B(字符串名称)
{
this.name=名称;
}
公共区域B()
{
}
}
静态void Main(字符串[]参数)
{
runService(“doWork”);
}
私有静态void运行服务(string methodName)
其中TService:class,new()
其中TArea:class,new()
{
//编译时处理
类型areaType=类型(跗骨);
类型serviceType=typeof(t服务);
//打印完整的程序集名称和限定的程序集名称
Console.WriteLine(“AreaType—完整程序集名称:\t{0}.”,AreaType.assembly.FullName.ToString());//打印完整程序集名称。
Console.WriteLine(“AreaType--限定程序集名称:\t{0}.”,AreaType.AssemblyQualifiedName.ToString());//打印限定程序集名称。
Console.WriteLine(“ServiceType—完整程序集名称:\t{0}.”,ServiceType.assembly.FullName.ToString());//打印完整程序集名称。
Console.WriteLine(“ServiceType--限定程序集名称:\t{0}.”,ServiceType.AssemblyQualifiedName.ToString());//打印限定程序集名称。
//之所以这样做,是因为在我的代码中,程序集不在Executy程序集中,它只是作为引用设置的
var assembly=assembly.Load(serviceType.assembly.FullName);
//初始化通用区域
TArea面积=默认值(TArea);
//获取该服务的实例,以便稍后调用该方法
var instance=Activator.CreateInstance(serviceType);
//获取提供给runService方法的methodName的methodInfo
MethodInfo-dfpMethod=serviceType.GetMethod(methodName,BindingFlags.Public | BindingFlags.Instance);
//area是类型强制转换为(TArea),intellisense显示属性“name”,但键入area.name不会给出值
//我必须键入((AreaA)area).name。问题是类型“AreaA”可能是另一种类型。在本例中,“AreaB”
area=(TArea)dfpMethod.Invoke(实例,新对象[]{“Area123”});
AreaA AreaA=(AreaA)dfpMethod.Invoke(实例,新对象[]{“Area123”});
Console.WriteLine();
}
}
}

我认为您在这里遇到的问题是将动态加载的类型(Assembly.Load())与直接从项目引用的类型混合在一起,您可以在intellisense中看到这些类型,即AreaA

如果您使用反射动态加载整个程序集,intellisense将不会帮助您查看类成员,因为这些信息需要在编译时知道,并且根据定义,您是在运行时加载程序集

如果您只想查看类型可用的所有公共属性的列表,则可以使用以下选项:

var areaProperties = area.GetType().GetProperties();
但是,这一切都是在运行时完成的,所以对您没有帮助
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();

        }

    }
}
var areaProperties = area.GetType().GetProperties();
var nameValue = area.GetType().GetProperty("name").GetValue(area);
TArea area = (TArea)dfpMethod.Invoke(instance, new object[] { "Area123" });
var area = (dynamic)dfpMethod.Invoke(instance, new object[] { "Area123" });
return area.name; // no error
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);