【问题标题】:How to cast an object as having a public parameterless constructor to respect a : new() constraint?如何将对象转换为具有公共无参数构造函数以尊重 : new() 约束?
【发布时间】:2013-11-14 04:21:37
【问题描述】:

是否有可能检查一个类型是否具有无参数构造函数,以便对其进行强制转换并调用需要具有: new() 约束的无参数构造函数的方法?

仅仅能够检查一个类型作为公共无参数回答here 是不够的,因为它不允许调用目标方法。

目标是有如下逻辑,其中IInteresting对象没有实现公共无参构造函数,需要在调用Save1之前进行转换:

    public interface IInteresting { }

    public void Save<T>(T o) {
        var oc = o as (new()); /* Pseudo implementation */
        if (oc != null) {
            this.Save1(oc);
        }
        else {
            var oi = o as IInteresting;
            if (oi != null) {
                this.Save2(oi);
            }
        }
    }

    private void Save1<T>(T o) where T : new() {
        //Stuff
    }

    private void Save2<T>(IInteresting o) {
        //Stuff to convert o to a DTO object with a public parameterless constructor, then call Save1(T o)
    }

当然,如果我可以让Save1Save2 共享相同的签名来解决这个问题,但我找不到这样做的方法,因为以下内容无法编译(RoutineSave将调用第一个实现而不是第二个):

    public void Routine<T>(T o) {
        var oi = o as IInteresting;
        if (oi != null) {
            this.Save(oi);
        }
    }

    private void Save<T>(T o) where T : new() {
        //Stuff
    }

    private void Save<T>(IInteresting o) {
        //Stuff to convert o to a DTO object with a public parameterless constructor, then call Save(T o)
    }

【问题讨论】:

  • "还不够,因为它不允许调用目标方法。" - 实际上,它会的。 ConstructorInfo 有一个 Invoke method
  • 只使用反射?但首先,重新考虑您的设计。
  • 你的 Save1 方法里面有什么?
  • Save 方法正在调用 ServiceStack.OrmLite 函数,该函数需要具有无参数构造函数的类型的对象。如果它还不是一个,我需要将对象转换为 DTO(尊重:new())。
  • @O.R.Mapper 我认为 ConstructorInfo 及其 Invoke 方法不允许我转换原始对象(仅创建一个新对象),对吗?

标签: c# interface casting signature type-constraints


【解决方案1】:

根据您的 cmets,我想您有一个未知类型的对象,您想将它传递给一个泛型函数,该函数要求传递的对象是泛型类型参数,该参数必须具有无参数构造函数。因此,我们暂时可以假设您的问题中的函数Save1&lt;T&gt;(T) 是该函数,不是您编写的,无法更改。

对此的解决方案是使用反射进行调用:

【讨论】:

  • 哇,确实可以做到这一点,尽管我希望有一种更开箱即用的方式来实现这一目标。
  • @ErwinMayer:因为泛型总是——除非你使用反射——必须在编译时解决,而你只在运行时知道你的类型,恐怕没有其他可行的方法。您可以添加到此解决方案的唯一轻微简化是使用一个泛型类,该类本身基于非泛型方法调用调用泛型方法 - 这样,您将跳过 GetMethod 步骤(基于字符串 -方法的名称,因此未经编译器检查),而是使用typeof(MyCallerType&lt;&gt;).MakeGenericType(o.GetType())
【解决方案2】:

另一个可能的解决方案是使用ICloneable 接口,具体取决于您在private void Save&lt;T&gt;(T o) where T : new() 中所做的事情。或者介绍一下你的(就像我说的,这取决于Save的内容):

interface IConstructible
{
    object Construct();
}

还有:

private void Save1<T>(T o) where T : ICloneable {

当然,这只是一种解决方法 - O. R. Mapper's answer 提供了唯一直接的解决方案。

【讨论】:

    【解决方案3】:
    using System.Reflection;
    
    
    public static class Generics {
     public static void T0<T> ( T obj ) where T : new () {
      Console.WriteLine ( "{0} {1}", obj, obj.GetType () );
     }
    
     public static void T1<T> ( T obj ) {
      MethodInfo mi = GenericMethodInfo ( typeof ( Generics ), "T0", typeof ( T ) );
      mi.Invoke ( null, new object[] { obj } );
     }
    
     public static MethodInfo GenericMethodInfo ( Type classType, string methodName, Type genericType ) {
      return classType.GetMethod ( methodName ).MakeGenericMethod ( genericType );
     }
    }
    
    
    Generics.T0 ( 123 );
    Generics.T1 ( 123 );
    // Impossible.. Generics.T0 ( "12345" );
    Generics.T1 ( "12345" );
    

    【讨论】:

    • 最好的答案有一些解释和代码。请考虑添加一些上下文。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-08
    • 2014-05-18
    • 1970-01-01
    相关资源
    最近更新 更多