【问题标题】:Combine GetType with Cast to convert List<IInterface> to List<Type>将 GetType 与 Cast 相结合,将 List<IInterface> 转换为 List<Type>
【发布时间】:2021-07-09 23:53:05
【问题描述】:

我知道此请求的反模式性质,但也许有更好的选择。

简介

我有List&lt;IBaseEntity&gt; 并想转换为List&lt;ActualEntity&gt;。尽管 ActualEntity 需要来自对 IBaseEntity 实际情况的反映。它是 50 多个潜在类别之一

所以是这样的:

public void Install(List<IBaseEntity> list)
{
    var type = list.First().GetType();
    var typedList = list.Cast<?>(?).ToList()
    ThirdPartyClass.Install( typedList )
}

只是不确定如何将type 放入Cast

原因详情

Install 方法必须是具体类型,因为第 3 方组件使用该类型链接到 EF 中的 DbSet。所以 BaseEntity 的列表不起作用。它抛出一个关于找不到DbSet&lt;IBaseEntity&gt; 的异常。必须是DbSet&lt;ConcreteClass&gt;

我保证 IBaseEntity 将始终是同一个具体类,但应用程序中的每个实体都会多次调用该方法。因此,通过指定每种具体类型来进行通常的转换是不切实际的。

EF 可以通过添加 DbContext.AddRange(list) 来确定 IBaseEntity 的实际含义并使用正确的映射。

提前致谢

** 更新 ** 有人建议调用方法会知道类型,不幸的是它有点复杂,所以让我解释一下。

有一个BaseSampleData&lt;TEntity&gt; : ISampleDataBuilder 类处理创建示例数据,它维护其他实体的主键列表等。

现在的症结是组装它们的工厂

public class ProjectASampleDataFactory
{
    public class List<ISampleDataBuilder> GetBuilders(){
        return new List<ISampleDataBuilder> {
            new UserSampleData(), // ISampleDataBuilder<User>
            new BlogsSampleData(), // ISampleDataBuilder<Blogs>
            etc...
        }
    }
}

问题是我需要一个 List 这是构建器的列表,但他们本质上并不知道他们的类型。所以调用者实际上并不知道。这 ???都是不同的(见上面代码块的cmets)

所以我得到了一个每个实体都遵循的列表,但是虽然它是一个列表,但我们只是不知道它是哪个 ActualEntity。

【问题讨论】:

  • 像public void Install&lt;T&gt;(List&lt;IBaseEntity&gt; list) where T : IBaseEntity 这样的东西能在这里工作吗?
  • @stuartd 我认为重点是ThirdPartyClass.Install 有一个List&lt;ActualEntity&gt; 作为参数。
  • 是的@Dominik,但这是否意味着在将参数传递给第三方之前可以调用Cast&lt;T&gt;()?
  • Install is List 但 T 需要是 ActualClass (与 DbSet 相同我无法控制此方法。MyListOfIBaseEntity.First().GetType() 不知何故需要被送入演员阵容的
  • stackoverflow.com/questions/232535/… 是答案......有人甚至可以写出如何为你打电话给Cast&lt;T&gt;......我也不会指望那个。

标签: c# interface casting


【解决方案1】:

你能控制这个方法的调用者吗?既然调用者可能已经知道它在处理什么类型,你能强制他们提供具体类型吗?

public void Install<T>(IEnumerable<IBaseEntity> list) where T:IBaseEntity
    => ThirdPartyClass.Install( list.Cast<T>().ToList() );

即使不能改变调用点,也可以通过反射调用你的方法;

public void Install(List<IBaseEntity> list)
{
    var type = list.First().GetType();
    new Action<IEnumerable<IBaseEntity>>(Install<IBaseEntity>)
        .Method.GetGenericMethodDefinition()
        .MakeGenericMethod(type)
        .Invoke(this, new object[] { list });
}

【讨论】:

  • 我喜欢这个...如果您将通用安装方法移动到安装方法主体内,我想我会更喜欢这个...
  • 你可以这样做,但我更愿意将非泛型方法标记为 Obsolete 以建议调用者转而调用泛型版本,因为调用者可能已经知道正确的类型。跨度>
  • 刚刚用 LinqPad 尝试过,似乎需要 C#8 才能工作,因为我需要将其定义为静态 void 以解决 Lambda 捕获问题。我正在发布包含模式匹配以确保列表不为空的版本...
  • 我添加了一些关于为什么我不能更改调用方法的注释,希望它有意义。我尝试了您的第二个选项,它看起来很有希望。它不能 100% 工作,但我正在检查库是否没有更新它正在安装的 ID,或者我调用它的方式。
  • 感谢您对此的帮助。最终,您已经回答了问题,我将查看主键返回问题,并在必要时单独询问。
【解决方案2】:

感谢 Jeremy Lakeman 通过 Action 委托访问 MethodInfo 的想法。

注意,这需要 C#8 才能工作,因为它需要静态 lambda 才能工作。

public void Install(List<IBaseEntity> list)
{
    if (!(list.FirstOrDefault() is {} first))
        return;
        
    static void InstallImpl<T>(List<IBaseEntity> list) where T : IBaseEntity
    {
        ThirdPartyClass.Install<T>(list.OfType<T>().ToList());
    }
    
    new Action<List<IBaseEntity>>(InstallImpl<IBaseEntity>)
        .Method.GetGenericMethodDefinition()
        .MakeGenericMethod(first.GetType())
        .Invoke(null, new[] {list});
}

【讨论】:

  • 我认为你的意思是“静态局部函数”,你总是可以让它成为 C# 的普通函数
  • @Charlieface 你需要然后将捕获传递给本地函数,这是完全不可能的......
  • 没看懂,这里没有抓包,就是普通参数
【解决方案3】:

您可能需要考虑在您的方法中使用非通用 List 以避免传递,例如将List&lt;object&gt; 转换为使用List&lt;string&gt; 的方法。

关键字dynamic 正是针对这种情况而设计的。

看看这个示例:

static void Main(string[] args)
{
    var objectList = new List<int>();
    objectList.Add(1);
    YourMethod(objectList);
}

static void YourMethod(IList objects)
{
    dynamic dynamicObjects = objects;
    FrameworkMethod(dynamicObjects);
}

static int FrameworkMethod(IList<int> typedList)
{
    return typedList.FirstOrDefault();
}

【讨论】:

  • 不幸的是,您示例中的框架方法是 IList 并且使用 IList 会导致无法识别 T 是什么的错误
  • @Simon 你当然也可以使用List&lt;T&gt; 而不是IList&lt;T&gt;
猜你喜欢
  • 2020-07-05
  • 1970-01-01
  • 1970-01-01
  • 2023-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多