【问题标题】:C# How to return an instance of the type of subclass from function?C#如何从函数返回子类类型的实例?
【发布时间】:2017-09-25 15:41:00
【问题描述】:

我有很多类可以制定各种不同的项目。我目前有这样的课程:

public class Item {
    public ItemFile file { get; set;}
    public ItemCalendar calendar { get; set;}
    public ItemWebsite website { get; set;}
}

ItemFile 等是使用实体框架创建的类,并映射到提供与该类型项目相关的信息的数据库表。 item 类只有一个实际实例化的内部属性。

我可以看到项目数量增长到大约 25 个或更多。让视图模型包含 25 个属性,其中 24 个为空,只有一个不为空,我觉得不合适。

我想要一些可以与实体框架一起使用并返回一个只能返回它的实际类型的类。因此,如果我要求更改项目,我会返回 ItemFile 文件和 ItemCalendar 日历。

我尝试过这样的事情:

public class Item
{
    public ItemBase item { get; set; }
}

public class ItemBase
{
    public Type typeName { get; set; }
    public object ItemInstance { get; set; }

    public typeName GetInstance()
    {
        return Convert.ChangeType(ItemInstance, typeName);
    }
}

但是我不知道如何将 ItemFile 作为 public typeName 返回是一个错误。

然后我尝试了:

public class Item
{
    public ItemBase<ItemFile> item { get; set; }
}

public class ItemBase<T>
{        
    public T ItemInstance { get; set; }
}

但要让它发挥作用,我必须在项目类的 中硬核 FileItem,这可以追溯到事先知道类型。

有没有办法让它工作?如果它可以与实体框架一起使用,因为我正在从那里撤回类,那么它可以加分。如果实体框架不能正常工作,最糟糕的情况是我可以将其全部提取出来,然后将其转换为回答问题的形式。

如果问题的标题有误,请随时编辑。我不知道怎么问。

tl;dr 版本:我希望能够使用不使用 传入的类型从函数返回多种类型的类。

编辑 1:

我忘了展示我的继承示例。我已经尝试过了,但也遇到了与上述类似的问题。

public class ItemBase
{
    public Type typeName { get; set; }
    public object ItemInstance { get; set; }

    public typeName GetInstance()
    {
        return Convert.ChangeType(ItemInstance, typeName);
    }
}

public class ItemFile : ItemBase

{
    public String FileName { get; set; }
}

public class Test
{
    public void testFunction()
    {
        //Made this just so the compiler didn't complain.
        ItemFile testFile = new ItemFile();

        //I can use a function to get the item base.
        ItemBase baseItem = testFile;

        //How do I do this? Use a function to get the ItemFile from the instance.
        ItemFile finalItem = baseItem.GetInstance();
    }
}

【问题讨论】:

  • 能展示一下你说的功能吗?
  • 您可能想查看继承(正如您的标签所建议的那样),但您的示例代码根本没有这样做。
  • @DavidG 我添加了我的继承示例。
  • 你的最后一行只需要ItemBase finalItem = (ItemBase)baseItem;
  • @DavidG 我相信你的意思是 ItemFile 而不是 ItemBase?但是,您提出的问题是我不知道它是 ItemFile 还是 ItemWebsite 等。我想要一个返回正确类型的函数。如果我想每次从数据库中提取它时手动检查并转换它,我相信我可以按照你的建议去做。

标签: c# entity-framework class inheritance


【解决方案1】:

我希望能够使用不使用 传入的类型从函数中返回多种类型的类。

(泛型)是函数可以显式返回多个类型的机制。如果没有泛型,该函数将返回它所说的任何类型。

object SomeFunction() // Returns an object

ItemBase SomeOtherFunction () // returns ItemBase

在上面的例子中,SomeFunction 仍然可以返回任何类型(因为所有类型都继承自object。)但它不会是显式的。你只知道这是一个object

同样,SomeOtherFunction 可以返回 ItemBase 或任何继承自 ItemBase 的类。但这就是你所知道的一切。

理想情况下,您不希望函数返回一种类型(如objectItemBase),然后将函数的结果转换为另一种更具体的类型。函数应该返回你想要的,你也应该想要函数返回的。泛型对此有所帮助。例如:

public TItem Instance<TItem>() where TItem : ItemBase

允许函数返回指定类型,只要它是ItemBase 或继承自一个。

这最后的评论看起来很奇怪或无用,但这是事实。如果您发现自己处于上述规则不起作用的情况下,并且您需要能够做一些您不能做或不应该做的事情,请回过头来重新考虑为什么要这样做。这就是真正的问题所在。

这意味着您可能需要退后一步,让自己摆脱试图与语言对抗的境地。您想要完成什么?如何以适合该语言而不是与之对抗的方式来完成它?

【讨论】:

  • 我喜欢“where”部分,您只将其限制为 ItemBase 类。我不知道你能做到这一点。我认为除了一部分之外,所有这些都会起作用。拿你提出的: public TItem Instance() where TItem : ItemBase.我怎么称呼它,让我们说 ItemFile?我相信它是 Instance()。在通话时我不知道硬核 ItemFile 是什么。但是我可以存储类型,我想知道您是否可以将类型变量传递给函数,所以 Instance()?我需要以某种方式从 itemWebsite 等 25 种类型中分辨出要使用的类型。
  • 我在想一个函数会起作用,因为我可以把所有的逻辑都说成如果这是一个 ItemFile 然后是 Instance() 但正如我在上面发布的那样,我将无法返回多个来自该函数的类型,所以我会在同一个地方。
  • 这是问题的核心:“我在调用时不知道硬核 ItemFile 是什么。”如果你正在调用一个函数并且你不知道你希望它返回什么类型,那么当你得到它时你怎么知道如何处理它呢?如果您不知道它是 ItemFile,则表明您不会调用特定于 ItemFile 的任何方法。那么为什么你需要它来返回那个特定的类型呢?这就是为什么我会从您尝试解决的问题中退后一步,并找出一种您不需要解决该问题的方法。
  • 这并不罕见。我们一直都在你现在试图解决几乎相同的问题的地方。所以当我说的时候相信我,答案不是解决它,而是设计代码,这样你就不需要解决这类问题。不管我怎么说,这似乎是幸运饼干的东西。但这是事实。
  • “如果你不知道它是一个 ItemFile,这表明你不会调用任何特定于 ItemFile 的方法。”我将调用特定于 25 种不同情况的方法。我猜那么最好的办法是将它全部存储为一个对象,检查类型以查看文件是什么类型,然后从该类型中创建一个 switch 语句,然后进行您发布的硬转换然后执行我的行动。唯一糟糕的是我必须在大约 7 个不同的文件中重现 switch 语句的内容。
【解决方案2】:

我相信这与您将要得到的差不多。

using System;
using System.Reflection;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            EFTypeData itemData = GetItemData();
            var asmName = Assembly.GetExecutingAssembly().GetName().Name;
            var type = Type.GetType($"ConsoleApplication1.{itemData.TypeName}, {asmName}");
            var instance = Activator.CreateInstance(type);
            var item = new Item<Object>()
            {
                ItemBase = instance 
            };
        }

        private static EFTypeData GetItemData()
        {
            return new EFTypeData() { TypeName = "ItemFile" };
        }
    }

    class EFTypeData
    {
        public string TypeName { get; set; }
    }

    class Item<T> where T: class
    {
        public T ItemBase { get; set; }
    }

    class ItemFile
    {
        public string FileName { get; set; }
    }
}

这将给定一个字符串“ItemFile”,创建一个实例并将其分配给 Item。如果你运行它并检查item,你有

对此的最大警告是,在编译时,您所拥有的只是一个作为 ItemBase 的对象。如果不对您的类型进行硬编码(即var item = new Item&lt;ItemFile&gt;();),您将永远不会知道更多。
也就是说,使用这种方法,您可以完全清楚地使用反射来迭代字段等。但这是此级别的运行时对象操作的限制。

【讨论】:

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