【问题标题】:How to fire an extension method by passing a generic type to a base class?如何通过将泛型类型传递给基类来触发扩展方法?
【发布时间】:2017-01-28 16:10:43
【问题描述】:

我想知道如何通过传递泛型类型从基类中访问扩展方法。

我有几个不同的类,它们都包含 ToModel 和 ToContract Funcs。 这些方法是从实体框架类型切换到数据契约,反之亦然。

他们都做同样的重复调用,所以我想浓缩一下代码。

我尝试过使用反射调用函数。和许多其他方法都无济于事。我在下面的代码中简化了我面临的问题。

我的问题是我无法从基类中访问扩展方法。请帮忙。

我收到错误:类不包含“ToModel”方法的定义。

简化代码

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestExtensions
{    
    public class Program : Base<Client>
    {
        static void Main(string[] args)
        {
            Client c = new Client();
            c.FirstName = "First Name";

            Console.WriteLine(c.FirstName);

            c.ToModel();

            Console.WriteLine(c.FirstName);

            Program p = new Program();
            p.go(c);
        }

        public void go(Client c)
        {
            base.ChangeNameAgain(c);
            Console.WriteLine(c.FirstName);

        }
    }
}

扩展类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestExtensions
{
    public static class ExtensionClass
    {
        public static Client ToModel(this Client c)
        {
            c.FirstName = "First Name Changed";

            return c;
        }
    }
}

客户端类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestExtensions
{
    public class Client
    {
        public string FirstName { get; set; }
    }
}

基类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestExtensions
{
    public class Base<T>
    {
        public T ChangeNameAgain(T c)
        {
            // This is where I need help
            // Need to invoke the ToModel Method using reflection               


            return (T) c;
        }

    }
}

我的实际代码

基础

public class BaseApi<TContractType, TModelType> : ApiController
    where TModelType : DataModelBase, IApiExtensionModel, new()
    where TContractType: DataContractBase, IApiExtensionContract, new()
{

 public TContractType Create(
        IGenericRepository<TModelType> repo, 
        TContractType obj)        
    {                        I   
                // This is what is giving me trouble               
                var instance = (TContractType)
                    Activator.CreateInstance(typeof(TContractType), 
                    new object[] { obj }); 

                var modelMap = instance.ToModel(); 
                var ret = (dynamic)repo.Edit(modelMap);                    
                return ret.ToContract();
    } 

}

实际有效的代码

尽量使其通用,因为它是重复的。

    [Microsoft.AspNetCore.Mvc.HttpPost]
    public patientContract.Patient CreatePatient(
        [Microsoft.AspNetCore.Mvc.FromBody] patientContract.Patient patient)
    {

            var map = patient.ToModel();
            var ret = _patientRepo.Add(map);
            return ret.ToContract();
    }

扩展

public static class PatientContractExtension
{
    public static Model.Patient ToModel(this Contract.Patient patientContract)
    {  
        var map = Mapper.Map<Contract.Patient, Model.Patient>(patientContract);

        return map; 
    }

    public static Contract.Patient ToContract(this Model.Patient patientModel)
    {
        var map = Mapper.Map<Model.Patient, Contract.Patient>(patientModel);

        return map;
    }                    

}

【问题讨论】:

  • 为什么你使用dynamic c而不是T c作为参数?
  • ToModelClient 上运行,因此您需要将c 声明为Client 或带有约束TClient 的通用TClient
  • @Theodoros 我不知道 TClient 会是这样,所以我不能将它指定为客户端类型
  • 整个架构让我想尖叫着逃跑。我从未见过有人试图以这种方式将Program 与另一种类型的行为结合起来。你正在建造一座纸牌屋。请重新审视“封装”和“单一责任”原则。

标签: c# generics dynamic extension-methods base-class


【解决方案1】:

动态类型不支持扩展方法(就像它们是实例方法一样)。编译器使用包含关键字this 的签名搜索所有具有公共静态方法的公共静态类,然后找到所有由此扩展方法扩展的类。

这不适用于动态,因为在编译时类型是未知的。如果你想使用扩展方法 - 将它与强类型成员一起使用。

您可以创建基客户端类:

public abstract class BaseClient 
{ 
  public string FirstName { get; set; }
}

public class Client : BaseClient
{
}

并使用泛型和泛型约束扩展基本类型,以避免向上转换为基本类型:

public static class ExtensionClass
{
    // we inform compiler that this method extends any type that is derived from base type
    public static TEntity ChangeName<TEntity>(this TEntity entity, string newName)
       where T : BaseClient            
    {
        entity.FirstName = newName;

        return entity;
    }

    public static TResult ToModel<TEntity, TResult>(this TEntity entity)
       where TEntity : BaseClient
       where TResult : BaseClient, new()
    {
        var result = new TResult();

        result.FirstName = entity.FirstName; 
        // ... rest of binding operations goes here

        return result;
    }
}

附: 目前尚不清楚为什么需要此 ToModel() 方法,因为在您提供的代码中只有一个模型 - Client1。你可以这样调用它:

var newModel = client.ToModel<SomeTypeDerivedFromBaseClient>();

【讨论】:

  • 好的,所以我要增加复杂性,我不知道 TEntity 会是 BaseClient 的,因为它也可能是另一种类型。我正在尝试在基类中调用该函数。
  • 这只是一个例子,你可以使用任何T1、T2类型
  • 这确实帮助了我,允许我添加:BaseType
【解决方案2】:

我已经通过将扩展方法移到我的基类中并使其成为常规方法来解决我的问题。

简单代码

public static T ToModel(T c)
{
    c.FirstName = "Extension Class";

    return c;
}

这样称呼

ToModel(c);

实际代码

private TModelType ToModel(TContractType contract)
{
    var map = Mapper.Map<TContractType, TModelType>(contract);
    return map;
}

private TContractType ToContract(TModelType model)
{
    var map = Mapper.Map<TModelType, TContractType>(model);
    return map;
}

然后可以这样称呼它们

var modelMap = ToModel(obj);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多