简体   繁体   English

如何创建C#泛型方法

[英]How to create a C# generic method

I have a method that I copy paste into many classes. 我有一种方法可以将粘贴复制到许多类中。 The method hydrates a model into an IdNameViewModel. 该方法将模型合并到IdNameViewModel中。 How do I create a class with this method that I can pass in the repository model and get back a list of IdNames for that model? 如何使用此方法创建一个类,我可以在存储库模型中传递该类并获取该模型的IdName列表?

Note: ProjectValue is a model/class that I'm passing in for example but it really could be any model/class that that contains an id and name property. 注意:ProjectValue是例如我要传递的模型/类,但实际上它可以是包含id和name属性的任何模型/类。

private IdNameVM HydrateEntityToVM(ProjectValue projectValue)
{
    if (projectValue == null) return null;

    return new IdNameVM()
    {
        Id = projectValue.Id,
        Name = projectValue.Name
    };
}

Here is what I came up with based on the answers below: 这是我根据以下答案得出的结果:

public class GenericHydrateIdName<TModel> where TModel : INameId
    {
        public IdNameVM HydrateEntityToVM(TModel model)
        {
            if (model == null) return null;

            return new IdNameVM()
            {
                Id = model.Id,
                Name = model.Name
            };
        }
    }
}

Just a note if anyone is following, the class above caused way too much work and violates the Clean Code Principle. 请注意,如果有人在追随,上面的类会导致过多的工作,并且违反了“清洁代码原则”。 I used the extension method below. 我在下面使用了扩展方法。

Basically, the best type-safe way I can see is having an interface. 基本上,我看到的最好的类型安全的方法是拥有一个接口。

interface INameId
{
    int Id { get; set; }
    string Name { get; set; }
}

You can have this interface in every class you would like to use it in. Based on your current usage, I would suggest an extension function 您可以在要使用它的每个类中使用此接口。根据您当前的用法,我建议使用扩展功能

static class VMExtensions
{

    public static IdNameVM HydrateEntityToVM<TModel>(this TModel model)
        where TModel : INameId
    {
        if (model == null) return null;

        return new IdNameVM()
        {
            Id = model.Id,
            Name = model.Name
        };
    }

    // update: without generics as Marcus Höglund pointed out
    public static IdNameVM HydrateEntityToVM2(this INameId model)
    {
        if (model == null) return null;

        return new IdNameVM()
        {
            Id = model.Id,
            Name = model.Name
        };
    }

    static void Test()
    {
        var model = new ProjectValue();
        var model2 = new AnotherModel();

        var viewModel = model2.HydrateEntityToVM();
        var viewModel2 = model2.HydrateEntityToVM();

    }

}

If you don't want to add the interface to all your models, you can use reflection (but note that it would be really slow). 如果您不想将接口添加到所有模型中,则可以使用反射(但请注意,这确实很慢)。

Update 更新资料

As Marcus pointed out, In this case, there is no real point of using generics in the function. 正如Marcus所指出的,在这种情况下,在函数中使用泛型没有任何实际意义。 You can use the HydrateEntityToVM2 implementation that directly uses the interface. 您可以使用直接使用该接口的HydrateEntityToVM2实现。 However, in a future point if you want to use something like IdNameVM<TModel> so you can still access the original model if required, HydrateEntityToVM function with generics will be helpful. 但是,在将来,如果要使用IdNameVM<TModel>类的东西,以便在需要时仍可以访问原始模型,则具有泛型的HydrateEntityToVM函数将很有帮助。

these are the basics of C#. 这些是C#的基础。 take a look at this link 看一下这个链接

class MyClass<T> where T : ProjectValue
{
    private IdNameVM HydrateEntityToVM(T projectValue)
    {
        if (projectValue == null) return null;

        return new IdNameVM()
        {
            Id = projectValue.Id,
            Name = projectValue.Name
        };
    }
}

in case if you don't know the abstraction of model (but be careful , this method can consume any object containing id and name public properties) , then try the following: 如果您不知道模型的抽象(但请注意,此方法可以使用任何包含id和name公共属性的对象),请尝试以下操作:

public void SomeGenericMethod<T>(T model) where T : class
{
    if (model == null) return null;

    var propId = model.GetType().GetProperty("id");
    var propName = model.GetType().GetProperty("name");
    if (propId == null || propName == null)
        return;


    return new IdNameVM()
    {
        Id = model.Id,
        Name = model.Name
    };
}

For creating a Generic Method, you have to keep in mind the few rules 创建通用方法时,必须牢记一些规则

  1. What Types you are going to support 您将要支持什么类型
  2. What will be the resultant 会有什么结果
  3. There must be singularity between the solutions 解决方案之间必须有奇异之处

Let's take the above mentioned example: You need a generic method of name HydrateEntityToVM . 让我们以上述示例为例:您需要一个名称为HydrateEntityToVM的通用方法。 The very first step is the declaration syntax of the method along and object casting from T type of Strong Type. 第一步是方法的声明语法以及从T类型的Strong Type进行对象强制转换。

Point to be noted here is you are passing a T type and want a resultant to be always IdNameVM class then your implementation should be as: 这里要注意的一点是,您正在传递T类型,并且希望结果始终为IdNameVM类,那么您的实现应为:

//The Method declaration of T type
private IdNameVM  HydrateEntityToVM<T>(T projectValue)
{
     //Checking whether the object exists or not
     //if null than return the default value of IdNameVM
     if(projectValue == null) return default(IdNameVM);  

     //Checking if we are supporting the implementation or not for that class
     //In your case YourStrongTypeClass is ProjectValue Class
     if(projectValue is YourStrongTypeClass) 
     {

         //Casting the T object to strong type object
         var obj = projectValue as YourStrongTypeClass;

         return new IdNameVM()
         {
            Id = obj.Id,
            Name = obj.Name
         };
     } 
     else
     {
        //The else statement is for if you want to handle some other type of T implementation
        throw new NotImplementedException($"The type {typeof(T)} is not implemented");
     }
}

I like Neville Nazerane's answer because removes the need for reflection. 我喜欢内维尔·纳泽拉内(Neville Nazerane)的回答,因为它消除了反思的需要。 However, if you did want to do this using reflection then below is a workable way that constructs the result even if it cannot find matching Id or Name properties. 但是,如果您确实想使用反射来做到这一点,那么即使找不到匹配的Id或Name属性,下面也是一种构造结果的可行方法。

Please note that in this sample I have not added any checking on whether the types for the Id and Name properties match between T and TResult. 请注意,在此示例中,我没有添加任何检查I和Name属性的类型在T和TResult之间是否匹配。

   public static TResult HydrateEntityToVM<T, TResult>(T inputValue, TResult resultValue) where T : class 
    {
        if (inputValue == null || resultValue == null)
            return default(TResult);

        var idValue = inputValue.GetType().GetProperty("Id").GetValue(inputValue);
        var nameValue = inputValue.GetType().GetProperty("Name").GetValue(inputValue);

        object instance;
        if (resultValue == null)
        {
            var ctor = resultValue.GetType().GetConstructor(Type.EmptyTypes);
            instance = ctor.Invoke(new object[] {});
        }
        else
        {
            instance = resultValue;
        }

        var idProp = instance.GetType().GetProperty("Id");
        if (idProp != null)
        {
            if (idProp.CanWrite)
                idProp.SetValue(instance, idValue);
        }

        var nameProp = instance.GetType().GetProperty("Name");
        if (nameProp != null)
        {
            if (nameProp.CanWrite)
                nameProp.SetValue(instance, nameValue);
        }

        return (TResult) instance;
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM