简体   繁体   English

C#如何在泛型类中访问泛型类型的成员?

[英]C# How to access member of generic type in a generic class?

    public class DS2DbContext : DbContext
    {
        public DbSet<DocumentFileData> DocumentFileData { get; set; }
        public DbSet<WatermarkFileData> WatermarkFileData { get; set; }
        public DS2DbContext(DbContextOptions<DS2DbContext> options) 
            : base(options) { }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
        }
    }

    public class FileDataController<T> : ODataController where T : class
    {
        private readonly DS2DbContext _context;
        private readonly ILogger<FileDataController<T>> _logger;

        public FileDataController(DS2DbContext dbContext,
                                  ILogger<FileDataController<T>> logger)
        {
            _logger = logger;
            _context = dbContext;
        }

        [EnableQuery]
        public SingleResult<T> Get([FromODataUri] Guid ID)
        {
            var result = _context.Set<T>().Where(i => i.ID == ID);
            // Error CS1061: 'T' does not contain a definition for 'ID'
            // and no accessible extension method 'ID' accepting a
            // first argument of type 'T' could be found
            return SingleResult.Create(result);
        }
    }

How do I properly (and if possible, elegantly) access members of variables with type T and get their values?如何正确(如果可能,优雅地)访问类型为 T 的变量的成员并获取它们的值?

Update更新

When I use an interface like this:当我使用这样的界面时:

    public class FileDataController<T> : ODataController where T : IFileData
    {
        private readonly DS2DbContext _context;
        private readonly ILogger<FileDataController<T>> _logger;

        public FileDataController(DS2DbContext dbContext,
                                  ILogger<FileDataController<T>> logger)
        {
            _logger = logger;
            _context = dbContext;
        }

        [EnableQuery]
        public SingleResult<T> Get([FromODataUri] Guid ID)
        {
            var result = _context.Set<T>().Where(i => i.ID == ID);
            // Error CS0452: The type 'T' must be a reference type in
            // order to use it as parameter 'TEntity' in the generic
            // type or method 'DbSet<TEntity>'            
            return SingleResult.Create(result);
        }
    }

I am getting an error when calling _context.Set().调用 _context.Set() 时出现错误。

What I have come up with is我想出的是

    var result = _context.Set<T>()
        .Where(x => (Guid) x.GetType().GetProperty("ID").GetValue(x) == ID);

But this looks horribly complicated.但这看起来非常复杂。

The story behind this这背后的故事

is that I have to store data of the exact same structure in two different database table depending on the data's semantics (requirements document or watermark).是我必须根据数据的语义(需求文档或水印)将完全相同结构的数据存储在两个不同的数据库表中。 Since I am doing Blazor, code first, I need to have two different classes for this to make it correctly create two tables and handle them via two different controllers.因为我在做 Blazor,代码优先,所以我需要有两个不同的类来使它正确地创建两个表并通过两个不同的控制器处理它们。 However, these controllers share the exact same code and the exact same underlying data structure.但是,这些控制器共享完全相同的代码和完全相同的底层数据结构。 Here is a simplified example just implementing an ID:这是一个仅实现 ID 的简化示例:

    public interface IFileData
    {
        public Guid ID { get; set; }
    }

    using System.ComponentModel.DataAnnotations;
    public class FileData : IFileData
    {
        [Key]
        public Guid ID { get; set; } = Guid.Empty;
    }

    public class DocumentFileData : FileData { }

    public class WatermarkFileData : FileData { }

Consequently, there is a controller for DocumentFileData and one for WatermarkFileData to access the proper database table for each type of data.因此,有一个用于 DocumentFileData 的控制器和一个用于 WatermarkFileData 的控制器,用于访问每种数据类型的正确数据库表。 However, all underlying operations are identical.但是,所有基础操作都是相同的。 So I hope to able to solve this via a generic class to save me the trouble of having to make identical changes to two different classes everytime something in file data handling changes.所以我希望能够通过一个通用类来解决这个问题,这样我就不必每次在文件数据处理中发生变化时都必须对两个不同的类进行相同的更改。

由于您有一个公开ID属性的通用接口,因此提供了相应的泛型约束 - (您需要同时指定泛型类型约束 - interface 和class ):

public class FileDataController<T> : ODataController where T : class, IFileData

As @GuruSiton correctly commented, you should introduce an interface with ID in it:正如@GuruSiton 正确评论的那样,您应该在其中引入一个带有ID的接口:

interface IClassWithId
{
    Guid ID { get; } // Notice that you only require get - the implementer can choose how the ID get init'd - private / c'tor / etc
}

public class FileDataController<T> : ODataController where T : IClassWithId
{ ... }

This assumes that you can make - or require - all classes used as T to implement IClassWithId .这假设您可以制作或要求所有用作 T 的类来实现IClassWithId If you're stuck with existing classes that define their own ID and can't change them, then you must resort to reflection, as hinted to by @NaeemAhmed.如果您被现有的定义自己的 ID 的类所困扰并且无法更改它们,那么您必须求助于反射,正如 @NaeemAhmed 所暗示的那样。

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

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