将通用对象传递给函数并获得智能感知

时间:2014-02-01 17:34:19

标签: c# generics types

我希望能够将已知类型传递给一般函数,但我收到编译错误,因为我无法在设计时转换类型。

考虑我的OnCreate功能:

示例1:

private void OnCreate<T>(T object)
{
   object.CurrentDate = DateTime.Now;
       object.SpecialProperty = "Hello";
}

示例2:

private void OnCreate<T>(T object)
{
   object.BirthDate = DateTime.Now;
       object.BirthdayMessage = "Happy Birthday";
}

我想调用OnCreate并传递给它一个对象。该对象恰好是MVC应用程序中的模型对象。我无法预测将什么模型对象传递给OnCreate但我想要访问传递的模型的唯一属性。如上面的示例所示,一个模型具有CurrentDate和SpecialProperty属性;另一个有BirthDate和BirthdayMessage属性。我不想为每个人创建一个特殊功能,因为我有许多不同的模型。此外,此OnCreate函数将从基类继承。这里的想法是提供一个&#34; hook&#34;进入控制器的Create方法,以便有人可以在将模型属性保存到数据库之前更改它们。换句话说,控制器的Create方法会将模型传递给我的OnCreate函数,然后在模型传回之前对模型进行一些工作。

正如您所料,每种型号都有不同的属性。由于这个要求,我意识到我无法通过OnCreate函数进行早期绑定并获得智能感知 - 但我的问题是编译器不会让我引用对象的属性,直到它知道对象类型。我无法在设计时投出它,因为直到运行时我才知道类型。

修改

我认为我的问题不是那么清楚,从答案来判断(对此我感激不尽 - 他们并不是我想要的)。或许最好展示我想如何调用OnCreate():

OnCreate(model);

现在,当OnCreate收到对象&#34; model&#34;时,它需要能够在该模型上设置属性。我想我可以在模型上使用反射并做类似的事情(这只是伪代码 - 仍在学习反射):

if typeof(model) is CustomerModel then
   (CustomerModel(model)).BirthDate = "1/1/1960";
   (CustomerModel(model)).BirthdayMessage = "Happy Birthday";
elseif typeof(model) is AnotherModel then
   (AnotherModel(model)).CurrentDate = DateTime.Now;
   (AnotherModel(model)).SpecialProperty = "Hello";
etc...

但我试图避免使用一堆if / then语句。我更喜欢这个电话可能是&#34;路由&#34;到一个特定于传递类型的函数。这样,对OnCreate的调用会将对象发送到重载(?),因此不需要反射逻辑......

第二次编辑

经过进一步思考(没有双关语),我不认为OnCreate函数中有一堆if / else语句是最好的方法。我提出了另一个可能最有效的想法,并且满足了我表达的愿望,即避免出现一堆if / then语句&#34; (在我的第一个编辑中指定):我的想法是让我的模型实现IOnCreate,它将提供.OnCreate()方法。因此,我的&#34;泛型&#34;实现IOnCreate的模型对象可以这样使用:

model.OnCreate();

然后OnCreate函数将知道模型上的属性:

public void OnCreate()
{
    this.BirthdayMessage = "Happy Birthday";
    etc...
}

我在这里看到两个问题:

1 - 在控制器中我需要测试模型是否实现了IOnCreate - 如果它没有,我就不会尝试调用OnCreate()。 2 - 我需要确保添加一个公共函数(如OnCreate())不会干扰EF6在代码优先项目中生成数据库表的方式。

我现在的问题是这种方法是否最佳......或者是否还有其他想法需要考虑......

2 个答案:

答案 0 :(得分:4)

由于T 任何类型,编译器不能指望它有CurrentDateSpecialProperty; 你可以尝试解决这个问题:

public interface IMyInterface {
  DateTime CurrentDate {get; set}
  String SpecialProperty {get; set}
}

public class MyClassA: IMyInterface {...}

public class MyClassB: IMyInterface {...}

public class MyClassC: IMyInterface {...}

...

private void OnCreate<T>(T value)
  where T: IMyInterface // <- T should implement IMyInterface
{
  value.CurrentDate = DateTime.Now;
  value.SpecialProperty = "Hello";
}

答案 1 :(得分:0)

根据您的示例,Generics似乎不太可能解决您的问题(您的应用),似乎使用了抽象层(InterfaceAbstract Class)更合适。

当使用“裸骨”泛型时,任何类型都可以传递给您的方法,因为.Net中的任何类型都是Object类型,您可以在这些泛型参数上执行任何与对象相关的功能。为了扩展泛型的这种能力来实现我们有Generics Constraints的继承层次结构中最基本的类型,那些允许你限制可以作为泛型参数传递的类型的范围。在您的情况下,您希望使用Type Constraints,它将类型范围限制为仅实现指定类型的类型。

例如,我们有A类型,A类型BC作为派生类,我们希望方法M仅接受输入如何实现A

class Program
{
    static void Main(string[] args)
    {
        M(new A()); // will work
        M(new B()); // will work
        M(new C()); // will work
        M(new D()); // wont work
    }

    public static string M<T>(T arg)
        where T : A
    {
        return arg.Data;
    }
}

public class A { public string Data { get; set; } }
public class B : A { }
public class C : B { }
public class D { }

修改

根据您的上一次编辑,您似乎有两种方法可以解决此问题。

实现抽象层(Interface):您可能希望添加一个接口并由模型实现。

public static void OnCreate(IBirthable arg)
{
    arg.BirthDate = ...;
}

接口:

public interface IBirthable
{
    DateTime BirthDate { get; set; }
    string BirthdayMessage { get; set; }
}

型号:

public class CustomerModel : IBirthable
{
    public DateTime BirthDate { get; set; }
    public string BirthdayMessage { get; set; }
}

public class AnotherModel : IBirthable
{
    public DateTime BirthDate { get; set; }
    public string BirthdayMessage { get; set; }
}

使用reflection:如果您选择不使用界面,可能与您的模型没有逻辑连接,您可能需要使用反射。

public static void OnCreate<T>(T arg)
{
        var type = arg.GetType();
        var birthDateProperty = type.GetProperty("BirthDate");
        if (birthDateProperty == null)
            throw new ArgumentException("Argument not is implementing the model");
        birthDateProperty.SetValue(arg, DateTime.Now);
        //And so on...
}

型号:

public class CustomerModel
{
    public DateTime BirthDate { get; set; }
    public string BirthdayMessage { get; set; }
}

public class AnotherModel
{
    public DateTime BirthDate { get; set; }
    public string BirthdayMessage { get; set; }
}