c# - 有没有一种方法可以使用对象的形状而不是继承来对通用函数参数进行类型检查?

标签 c# .net generics

我有一些想要创建的 EF 模型类。每个类都有一些我想在插入新实体之前设置的通用属性,例如:

public partial class BlogPost {
  public DateTime CreatedTime { get; set; }
  public string CreatorName { get; set; }
  public string PostTitle { get; set; }
  public string PostText { get; set; }
}

public partial class Comment {
  public DateTime CreatedTime { get; set; }
  public string CreatorName { get; set; }
  public string CommentText { get; set; }
}

...

当我创建这些类时,我会像这样实例化它们:

var blogPost = new BlogPost {
  CreatedTime = DateTime.UtcNow,
  CreatorName = creatorName,
  PostTitle = postTitle,
  PostText = postText,
};

var comment = new Comment {
  CreatedTime = DateTime.UtcNow,
  CreatorName = creatorName,
  ...
};

...

我想创建一个方法来自动设置一些常见属性,这样我就不需要为具有相同属性的每个类手动键入它们。由于它们不扩展相同的类或实现相同的接口(interface),我想知道如何实现这一点。我的第一个想法是使用通用方法;但是,我不知道是否有一种方法可以指定泛型类型应该具有哪些属性,而无需它们扩展同一个类(类似于 TypeScript 的 "duck typing" )。我想要的方法如下所示:

public void SetInitialProperties<T>(T dbEntity, DateTime createdTime, string creatorName) where T : ??? {
  dbEntity.CreatedTime = createdTime;
  dbEntity.CreatorName = creatorName;
}

...

var blogPost = new BlogPost { PostTitle = postTitle, PostText = postText };
SetInitialProperties(blogPost, createdTime, creatorName);

最坏的情况是,如果我不能使用泛型,我总是可以使用动态;但是,如果可能的话,我想继续进行类型检查。

最佳答案

你可以使用反射来实现你想要的。您可以传入一个对象并解析它的类型,然后获取该给定类型的所有公共(public)属性,并查找是否有一个名为 CreatedTime 的属性。然后,您可以在传递的 dbEntity 对象上设置给定属性的值。但是,我不推荐这种解决方案:

public void SetInitialProperties(object dbEntity, DateTime createdTime, string creatorName) {
        // get the passed object's properties and find the one called CreatedTime
        var createdTimePropertyInfo = dbEntity.GetType().GetProperties().Where(i => i.Name == "CreatedTime").FirstOrDefault();
        // below line is equal to: dbEntity.CreatedTime = createdTime;
        createdTimePropertyInfo.SetValue(dbEntity, createdTime);
        
        var creatorNamePropertyInfo = dbEntity.GetType().GetProperties().Where(i => i.Name == "CreatorName").FirstOrDefault();
        creatorNamePropertyInfo.SetValue(dbEntity, creatorName);
    }

从长远来看,通过创建通用接口(interface)甚至抽象基类,您的情况会更好,这样您就不必为每个 EF 模型实现 CreatedTime 和 CreatorName 以及其他属性。它看起来像下面这样:

public interface IUserEntry
{
    DateTime CreatedTime { get; set; }
    string CreatorName { get; set; }
}

public abstract class UserEntryBase : IUserEntry
{
    public DateTime CreatedTime { get; set; }
    public string CreatorName { get; set; }
}

public partial class BlogPost : UserEntryBase
{
    public string PostTitle { get; set; }
    public string PostText { get; set; }
}

public partial class Comment : UserEntryBase
{
    public string CommentText { get; set; }
}

您的 SetInitialProperties 将非常简单:

public void SetInitialProperties(IUserEntry dbEntity, DateTime createdTime, string creatorName)
    {
        dbEntity.CreatedTime = createdTime;
        dbEntity.CreatorName = creatorName;
    }

一旦开发出接口(interface),您将获得比使用反射或动态类型更大的灵活性,因为您可以获得我之前提到的编译时检查,并且可以看到模型的公共(public)属性。

关于c# - 有没有一种方法可以使用对象的形状而不是继承来对通用函数参数进行类型检查?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69622708/

相关文章:

c# - 在 C# 中,如何检查二维数组中的所有元素是否相等

c# - 将此代码从 C# 转换为 VB.NET

c# - WPF 数据网格 : modify all selected items

C#要在类中使用泛型委托(delegate)也有泛型

c# - C# "using"语句什么时候最有用?

c# - 使用 q 退出程序

c# - 使用表达式 lambda 进行异常处理

c# - 调用派生类的非继承方法

java - 泛型中的类型安全转换

swift - 使类扩展符合通用协议(protocol)功能