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

时间:2013-04-04 14:55:59

标签: c# dynamic reflection constraints

我有一些具有公共属性的类,但是,我不能使它们从基类型派生(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但是,我正在寻找一种方法来限制代码在编译时中使用此方法来处理没有{{ 1}}属性。

5 个答案:

答案 0 :(得分: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上是否存在属性。你必须使用反射 - 这很慢,显然不理想。

答案 1 :(得分: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;
}

答案 2 :(得分:2)

无法保证类型具有给定成员而不约束到公共基本类型或接口。解决此限制的一种方法是使用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);

答案 3 :(得分: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为“free”。

答案 4 :(得分:0)

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