2

我有一些具有公共属性的类,但是,我不能让它们派生自基类型(LINQ-to-SQL 限制)。

我想将它们视为具有基本类型,但不是通过使用反射(性能至关重要)。

例如:

public class User
{
    public int Id { get; set; }
    public string FirstName { get; set; }
}

public class Vehicle
{
    public int Id { get; set; }
    public string Label { get; set; }
}

在这种情况下,如果我有Id可用的财产,我会很高兴,无论我持有什么类型。

在 C# 中有什么方法可以实现类似的东西:

public static int GetId<T>(T entity) where T // has an int property 'Id'
{
    return entity.Id;
}

我想我可以使用dynamic,但是,我正在寻找一种方法来限制编译时的代码,不要将此方法用于没有Id属性的对象。

4

5 回答 5

4

您可以使用接口:

public interface IHasId
{
    int Id { get; }
}

public class User : IHasId { ... }
public class Vehicle : IHasId { ... }

public static int GetId<T>(T entity) where T : IHasId
{
    return entity.Id;
}

但是,如果您无法修改类以添加接口,您将无法执行此操作。没有编译时检查将验证属性是否存在于T. 你必须使用反射——这很慢而且显然不理想。

于 2013-04-04T14:57:31.980 回答
3

没有办法保证一个类型有一个给定的成员而不限制到一个公共的基类型或接口。解决此限制的一种方法是使用 lambda 访问该值

public static int Use<T>(T value, Func<T, int> getIdFunc) { 
  int id = getIdFunc(value);
  ...
}

Use(new User(), u => u.Id);
Use(new Vehicle(), v => v.Id);
于 2013-04-04T14:58:43.833 回答
2

您可以创建一个具有公共属性的接口并让您的类实现它:

public interface IEntity
{
    int Id { get; set; }
}

public class User : IEntity
{
    public int Id { get; set; }
    public string FirstName { get; set; }
}

public class Vehicle : IEntity
{
    public int Id { get; set; }
    public string Label { get; set; }
}

public static int GetId<T>(T entity) where T : IEntity
{
    return entity.Id;
}

你可以这样简化GetId

public static int GetId(IEntity entity)
{
    return entity.Id;
}
于 2013-04-04T14:57:22.390 回答
1

提到接口方法的其他答案当然很好,但我想根据您涉及 Linq-to-SQL 的情况定制响应。

但首先,按要求解决问题标题

可以在没有基类型的情况下使用 C# 约束吗?

一般来说,答案是否定的。具体来说,您可以使用structclassnew()作为约束,这些在技术上不是基本类型,它们确实为如何使用该类型提供了一些指导。这并没有完全达到您希望做的水平,即将方法限制为具有特定属性的类型。为此,您需要限制到特定的接口或基类。

对于您的特定用例,您提到了 Linq-to-SQL。如果您正在使用为您生成的模型,那么您应该可以选择修改这些类,而无需直接修改生成的模型类文件。

你可能有类似的东西

// code generated by tool 
// Customer.cs
public partial class Customer // : EntityBaseClasses, interfaces, etc
{
    public int ID 
    {
        get { /* implementation */ }
        set { /* implementation */ }
    }
}

以及其他类似的文件,例如帐户或订单或类似性质的东西。如果您正在编写希望利用常用 ID 属性的代码,您可以利用partial定义partial class第二类文件来为这些模型引入一个通用接口类型。

public interface IIdentifiableEntity
{
    int ID { get; }
}

这里的美妙之处在于使用它很容易,因为实现已经存在于您生成的模型中。你只需要声明它,你可以在另一个文件中声明它。

public partial class Customer : IIdentifiableEntity { }
public partial class Account : IIdentifiableEntity { }
// etc. 

当使用存储库模式并希望定义通用方法而不必在存储库中重复相同的样板时,这种方法对我很有价值。GetById存储库。我可以将方法/类限制在接口上,并GetById“免费”获得。

于 2013-04-04T15:26:40.753 回答
0

要么你需要让两个类都实现一个具有你需要的属性的接口,并在泛型约束中使用它,要么你为每种类型编写单独的方法。这是获得编译时安全的唯一方法。

于 2013-04-04T14:57:33.130 回答