【问题标题】:Dynamically pass class and method names to a method using generics使用泛型将类和方法名称动态传递给方法
【发布时间】:2014-02-23 08:12:01
【问题描述】:

我想将类名和方法名动态传递给方法并保持动态,我知道我应该使用泛型和可能的约束。

例如,我有一堂课

MemberRequestDTO    (contains several properties)

我还说一个方法叫

RecordsToRetrieve

使用一些反射,我想动态获取属性的值,我想出了如何做到这一点,但后来我意识到这是太难的代码并且紧密耦合,我想是时候重构并创建一个具有使用带有约束的泛型的签名的方法。在理解使用和约束等方面有困难。

所以我想传入一个类名并能够在方法中使用它,通过反射我打算像这样使用它:

Type type = typeof(classname);

我开始阅读和研究,并开始使用这样的代码:

public void GetTypeValues<T>() where T : class , new()
  1. 如何传入 MemberRequestDTO 的类名?
  2. Generic 对我有什么新作用?
  3. 如何将类名传递到括号 () 中?
  4. 如果我使用它是否也会传递到括号中?
  5. 如何传入类和方法?
  6. 阅读上面的“哪里 T 有约束(强制)为“类 AND new()”类型?

对此有点迷茫和困惑,请见谅。

编辑:

根据答案和一些研究,我对此有了更多的理解:

让我们忘记我试图传递一个方法,说我只想传递一个类

说有属性的类是这样的

 public class MemberRequestDTO
 {

    public DateTime DateRequested { get; set; }

    public string FirstName { get; set; }

    public string LastName { get; set; }
  }

那我就更新一下

var memberRequestDTO = new MemberRequestDTO();

然后我想把这个传递给类,变成一个通用的方法

如何将对象的实例传递给泛型方法?那么签名呢,例如 public void GetTypeValues() where T : class , new()
我想拥有 class 和 new() 的约束吗?

对于上述情况,T 是类的实例吗?因此目的是我可以成为

GetTypeValues(memberRequestDTO)      

(这是我的实际问题,传入我实例化的任何类,让方法“处理”处理该类,循环遍历属性并动态获取属性的名称值是的,它可能不会是一个 void 方法)

传入memberRequestDTO 应该带引号还是不带引号?我希望能够将类的任何实例传递给成员,然后对其进行更多操作。 () 应该在那里吗? parens () 应该为空还是包含类对象的通用参数?

【问题讨论】:

    标签: c# generics constraints


    【解决方案1】:

    这里是你的答案:

    1. GetTypeValues&lt;MemberRequestDTO&gt;()
    2. new()Type Parameter - T 的约束。它说类型参数T 必须有一个公共的无参数构造函数。在您的情况下,MemberRequestDTO 类必须是一个公共的无参数构造函数,如下所示:

      public class MemberRequestDTO
      {
          public MemberRequestDTO() { ... }
      }
      
    3. 由于类名是reference type,您可以将其作为type 传递到括号中,例如:SomeMethod(typeof(MemberRequestDTO));,其中方法的签名是void SomeMethod(Type type) { }

    4. 如果你将类作为type parameter 传递给第 (1) 点,它不会被传递到 parens()

    5. class 约束意味着“类型参数必须是引用类型;这也适用于任何类、接口、委托或数组类型。” 而new() 约束意味着“类型参数必须有一个公共的无参数构造函数。当与其他约束一起使用时,必须最后指定 new() 约束。”


    编辑:

    如果我明白你的意思,那么通用方法定义将类似于:

    public void GetTypeValues<T>(T typeObject) where T : class
    {
        // typeObject specific operations
    }
    

    动态使用typeObject,让“执行时编译器”执行类型推断以计算出TSee the reference here. 此外,恕我直言,您不需要在这里对T 进行new () 约束。

    之后,您可以将任何类的实例传递给该方法,如下所示:

    var memberRequestDTO = new MemberRequestDTO();
    GetTypeValues((dynamic) memberRequestDTO);
    

    编辑 2:

    用法:使用反射动态获取类型值

    此方法返回包装到IEnumerable&lt;KeyValuePair&lt;string, object&gt;&gt; 中的属性值。

    public static IEnumerable<KeyValuePair<string, object>> GetTypeValues<T>(T typeObject) where T : class
    {
        // typeObject specific operations
        IEnumerable<KeyValuePair<string, object>> typeValues = 
            typeObject
            .GetType()
            .GetProperties()
            .Select(property => new KeyValuePair<string, object>(property.Name, property.GetValue(typeObject)));
        return typeValues;
    }
    

    【讨论】:

    • 我在我的问题中添加了更多信息。希望它更有意义。
    • 我已经更新了上面的答案。请查看EDIT 部分。并为这个好问题 +1!
    • 如何使用 typeObject,示例 Type type = typeof(typeObject) 这无法解决...我正在使用此签名 public void GetTypeValues(T typeObject) where T : class
    • 我已经更新了我的答案。请查看EDIT 2。如果您有任何进一步的疑问,请随时询问。谢谢!
    • 不知道为什么要转换为动态((动态)memberRequestDTO。由于您将泛型指定为 然后在签名中使用 T,编译器足够聪明,可以找出 T是你传入的任何类型。即使你没有使用 T 作为参数,你也可以说 GetTypeValues,但你永远不必将它转换为动态的。
    【解决方案2】:

    如何传入 MemberRequestDTO 的类名?

    你已经有一个了。在这种情况下,在泛型方法“类型参数”T 将是您感兴趣的类型的名称。

    public void GetTypeValues<T>() where T : class , new()
    {
        string typeName = typeof(T).Name;
    }
    

    Generic 对我有什么新意?

    这是一个约束,它会阻止你在没有公共无参数构造函数的情况下传递任何类型。换句话说,它将允许您将新类型作为“类型参数”传入

    public void GetTypeValues<T>() where T : class , new()
    {
        T instance = new T();//This is not possible without new constraint
    }
    

    如何将类名传递到括号 () 中?

    如果我使用它是否也会传入括号?

    不确定parens() 是什么@ 需要更多信息来回答这个问题。

    如何传入类和方法?

    如果我理解正确,“类型参数”T 是您使用的运行时类型。所以你在那里得到一个Type。不知道你所说的类是什么意思?类不能传,只能传实例。

    对于方法,有多种方法。您可以传递MethodInfo 或方法名称或A 委托,或MethodCallExpression 等。

    阅读上面的“哪里 T 有约束(强制)是 输入“类 AND new() ?

    是的。类约束阻止您传递值类型,new() 约束允许您更新事物。

    阅读更多关于泛型herehere

    【讨论】:

    • “T”是否代表我新建一个实例并传入的 MemberRequestDTO?我意识到我不想做的是硬编码,因此 Type type = typeof(MemberRequestDTO);不好,我想使用反射来结束 typeof(T) - T 应该是 MemberRequestDTO 的实例,然后,我进一步不想使用 sbBuilder.Append(value +" :" + getFalpaRequest.FirstName).AppendLine();因为我想要一个泛型来确定属性名称的值。
    【解决方案3】:

    我对你想做什么有点困惑,但我会试一试。我可以看到两种可能的解释,它们在调用者开始的内容和您要达到的目标方面有所不同。

    解释 #1:调用者一开始就知道类的 name 和稍后要调用的方法的 name,使用它手头的对象.这可以通过以下方式实现:

    public Func<object, object> RecordMethod(string typeName, string methodName)
    {
        var type = Type.GetType(typeName);
        var method = type.GetMethod(methodName);
        return (object o) => method.Invoke(o, new object[0]);
    }
    
    var method = RecordMethod("MemberRequestDTO", "RecordsToRetrieve");
    
    // later that day ...
    MemberRequestDTO someObj = ...;
    var result = method.Invoke(someObj);
    

    如果您需要动态使用类型名称和方法名称,这很好,例如从用户输入。请注意,此方法需要始终使用object,并且仅适用于不带参数的方法。另请注意,这种方式不能保证类型具有无参数构造函数,因此调用者必须自己提供对象。

    解释 #2:调用者一开始就知道 actual 类和它想要稍后调用的 actual 方法,使用可以稍后构造的对象。这可以通过以下方式实现:

    public Func<TOutput> CaptureMethod<TInput, TOutput>(Func<TInput, TOutput> method) 
    where TInput : new()
    {
        return () =>
        {
            var source = new TInput();
            return method(source);
        };
    }
    
    var capturedMethod = (MemberRequestDTO dto) => dto.RecordsToRetrieve();
    
    // later that day ...
    var result = capturedMethod();
    

    这会捕获一个已知方法并返回一个函数,当调用该函数时,它将实例化您的类并在其上调用该方法。这是一种更加静态的方法(调用者知道的比上一个示例更多),并且能够强制执行一个约束,即所使用的类型必须具有无参数构造函数。

    我不知道我是否已经回答了你的问题,但这至少应该给你一些想法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-23
      • 2013-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多