【问题标题】:Return instance using reflection in C#在 C# 中使用反射返回实例
【发布时间】:2014-09-04 19:41:48
【问题描述】:

下面给出了我尝试返回类实例的示例代码。

public object getConstructorclass(int i)
{  
    if(i==1)
    {
       Type type = Type.GetType("test1");
    }else
    {
       Type type = Type.GetType("test2");
    }
    return Activator.CreateInstance(type); 
}    

var objcls = getConstructorclass(1);
objcls.callclass();//error occured

我怎么能在这里提到类类型,因为类型在编译时是未知的,但它会在运行时决定。在上面的例子中,我只是传递一个值 1(它可以是任何东西,并且该类将被相应地调用) ,并调用了 test1 类。

这里我会在objcls.callclass() 行收到一个错误,因为objcls 是一个没有callclass() 方法的object 实例。

如何重构这段代码?我的目标是,如果我在 getConstructorclass() 方法中提到一个类,则应该返回一个对象,以便在进一步的代码中使用它来调用该类的成员。

【问题讨论】:

  • 我能问一下你想通过这样做来完成什么吗? (顺便说一句,从你的例子中你知道类型是 test1 是什么)
  • 小心带有Activator 的命名空间。一般来说,您应该可以执行上述操作。但是,您可以为希望动态构造的对象集提供一个基类,然后将 getConstructorClass() 方法的返回类型限制为该基类,使其类型安全。
  • 我不能保证总是会调用 test1。更新的问题包括更好地了解我的场景。

标签: c# asp.net reflection


【解决方案1】:

如果你知道你的类会有这个方法,你应该为它们使用一个通用接口并相应地实现它。然后,您将使用您已确保它可以工作的类。

应该是这样的

IMyInterface objcls = getconstrorclass() as IMyInterface;
if (objcls != null)
    objcls.callclass();
else
    // we failed miserably and should do something about it

如果你的类没有任何共同点,我认为你不应该使用一些基于 int 变量的通用对象返回构造函数。像这样处理它真的很奇怪,它可能会导致各种问题(其中一些你目前已经遇到了)。如果类有些相关并且您可以预测结果,则通用类构造函数是有意义的,但是要创建一个全能方法。. 不太确定这种方法的正确性。

无论如何,如果你坚持(不推荐,但如你所愿),你可以为这样的类型创建一些检查:

var createdObject = getConstructorclass(1);
if (createdObject is MyClass1)
{
    var specificObject = (MyClass1)createdObject;
    specificObject.callMethod1(); 
}
else if (createdObject is MyClass2)
{
    var specificObject = (MyClass2)createdObject;
    specificObject.callSomeOtherMethod();
}
...

但它很快就会很容易出错,重构可能是一场噩梦等,但这是你的决定..

或者您也许可以使用 pwas 的解决方案,但在我看来,对于这样一个基本任务来说,这似乎是不必要的复杂。看起来不错,但它仍然只返回“object”类型,所以它并不能真正解决您的具体问题。

另外,为了解决一个问题,我不确定您是否理解 - 您已经创建了实例,您只需返回类型对象。这就是为什么您不能在此对象上调用任何特定方法的原因,因为首先您必须将其转换为实际上具有该方法的东西并确保可以完成转换(继承等)。

【讨论】:

  • 你比我先到了那里。 :-)
  • 我将有多个类并且需要动态调用这些类。即 var objcls = getConstructorclass(1); ,objcls 应该包含我动态调用的类的实例。
  • @user1648142,你的问题是什么?您仍然可以使用此解决方案,只需根据您的需要进行调整...
  • 您能否详细说明如何使用通用界面来满足我的要求?就我而言,我将有多个类,并且取决于作为输入提供的版本,这些类将被调用。此外,这些类可能包含具有相同签名的相同函数,因此我猜无法使用接口。如果我错了,请纠正我。
  • @user1648142,只要目标类实现了该方法,它将调用哪个类并不重要。这就是接口发挥作用的时候,因为如果你的类从这个接口继承,你保证你可以调用那个方法。您的示例代码显示相同。您正在实例化更多类型的类,但您仍然尝试调用该方法 => 使用包含它的接口并使您的类实现该接口。
【解决方案2】:

如果interface 解决方案(见其他答案)就足够了,不要看这个答案。当您不能使用通用基类/接口并且仍需要调用成员时,可以使用带有is 关键字的解决方案(并检查类型)。您可以使用流畅的 API,而不是为每种情况编写许多 ifs:

object obj = this.getConstructorclass();
obj.StronglyInvoke()
   .When<int>(value => Console.WriteLine("Got {0} as int", value))
   .When<string>(value => Console.WriteLine("Got {0} as string", value))
   .OnFail(() => Debug.Write("No handle."))
   .Invoke();

解决方案:

public class GenericCaller
{
    private IList<GenericInvoker> invokers = new List<GenericInvoker>();

    private readonly object target;

    private Action failAction;

    public GenericCaller(object target)
    {
        if (target == null)
        {
            throw new ArgumentNullException("target");
        }

        this.target = target;
    }

    public GenericCaller OnFail(Action fail)
    {
        this.failAction = fail;
        return this;
    }

    public GenericCaller When<T>(Action<T> then)
    {
        if (then == null)
        {
            throw new ArgumentNullException("then");
        }

        var invoker = new GenericInvoker<T>(this.target, then);

        this.invokers.Add(invoker);

        return this;
    }

    public void Invoke()
    {
        if (this.invokers.Any(invoker => invoker.Invoke()))
        {
            return;
        }

        if (this.failAction == null)
        {
            throw new InvalidOperationException("Handler not found");
        }

        this.failAction();
    }

    public abstract class GenericInvoker
    {
        protected readonly object target;

        protected GenericInvoker(object target)
        {
            this.target = target;
        }

        public abstract bool Invoke();
    }

    public class GenericInvoker<T> : GenericInvoker
    {
        private readonly Action<T> then;

        public GenericInvoker(object target, Action<T> then)
            : base(target)
        {
            this.then = then;
        }

        public override bool Invoke()
        {
            if (this.target.GetType() == typeof(T))
            {
                this.then((T)this.target);
                return true;
            }

            return false;
        }
    }
}

public static class Extensions
{
    public static GenericCaller StronglyInvoke(this object o)
    {
        return new GenericCaller(o);
    }
}

记住 - 使用通用界面会更优雅(正如其他答案所说) - 我是唯一的替代方式。

【讨论】:

    【解决方案3】:

    将您的变量声明为动态

    dynamic objcls = getconstrorclass();
    

    无论getconstrorclass 方法返回什么,使用它都将在运行时确定。您可以访问该类型的任何成员,并且在编译时不会出现任何错误。但是,如果您尝试访问不存在的成员,您将在运行时收到 RuntimeBinderException

    【讨论】:

    • 这在技术上确实有助于 OP,但它很可能无法解决根本问题。至少解释一下dynamic 做了什么,以及在没有该方法的类型上调用方法的陷阱是什么。
    【解决方案4】:

    我建议使用interface 并将可以通过这种方式实例化的类限制为仅实现接口的类。

    public interface IMyInterface
    {
        void callclass();
    }
    
    public <T> getConstructorClass()
    {
        T instance;
        Type type = Type.GetType("test1");
    
        // instance will be null if the object cannot be cast to type T.
        instance = Activator.CreateInstance(type) as T;
        return T;
    }
    
    IMyInterface objcls = getConstructorClass<IMyInterface>();
    if(null != objcls)
    {
        objcls.callclass();
    }
    

    【讨论】:

    • so 我如何在 IMyInterface objcls = getConstructorClass(); 行中使用反射我不能在这里提到 IMyInterface 因为我将有多个类并且我不能循环遍历条件所以取决于输入我将创建一个类实例。 IMyInterface 应该使用反射来定义。
    • @user1648142 下面的answer,来自 pwas 使用模板化泛型,而不是显式反射。它看起来是一个不错的解决方案,如果你真的,绝对不能使用界面。
    【解决方案5】:

    不确定您最终想要实现什么,但这看起来像是“依赖注入”的工作 - 这是使用 autofacnice sample

    【讨论】:

      猜你喜欢
      • 2018-03-29
      • 2021-10-16
      • 1970-01-01
      • 2021-09-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-28
      相关资源
      最近更新 更多