简体   繁体   English

如何将 class 名称用作通用参数?

[英]How is a class name used as a generic parameter?

I understand generics in C# and they are used to keep things strongly typed.我在 C# 中了解 generics,它们用于保持强类型。 I'm using Entity Framework and I'm seeing the < > syntax used, but it seems to be naming a parameter for a method as opposed to a type.我正在使用 Entity Framework,我看到使用了 < > 语法,但它似乎是在为方法而不是类型命名参数。
For example例如

services.AddDbContext<MyDbContext>(options => options.UseSqlServer(...));

MyDbContext is not a type, it's a class needed to link the DB and Models together. MyDbContext 不是一种类型,它是将数据库和模型链接在一起所需的 class。 But how is the syntax used here?但是这里的语法是如何使用的呢? Shouldn't it be....难道不应该……

 services.AddDbContext(MyDbContext);

Clearly I'm not understanding something about syntax here.显然我不了解这里的语法。

Broadly speaking, unless you've changed them, in recent VS properties would be colored white, types a sort of green and methods yellow.从广义上讲,除非您更改了它们,否则在最近的 VS 属性中,颜色为白色,类型为绿色,方法为黄色。

If you take a look at the MyDbContext in the angle brackets of the first line of code, it will be a greenish kind of color:如果您看一下第一行代码尖括号中的 MyDbContext,它将是一种偏绿色的颜色:

services.AddDbContext<MyDbContext>(...)
                      ^^^^^^^^^^^
                   green means type

And in the second it would be white meaning a property representing some instance of a context somewhere (property because it's PascalCase not camelCase):在第二个中,它将是白色的,表示表示某处上下文的某些实例的属性(属性,因为它是 PascalCase 而不是 camelCase):

services.AddDbContext(MyDbContext)
                      ^^^^^^^^^^^
                   white means property

AddDbContext doesn't need an instance; AddDbContext 不需要实例; it doesn't want an instance.不需要实例。 An important part of what it does is set up dependency injection so that the injector provides instances of a db context: it just wants a type parameter so it knows what type of instances to provide (and how they're configured, options wise etc, what kind of database connection to use etc).它所做的一个重要部分是设置依赖注入,以便注入器提供数据库上下文的实例:它只需要一个类型参数,这样它就知道要提供什么类型的实例(以及它们是如何配置的,明智的选择等,使用什么样的数据库连接等)。 Once it knows this it can eg create an instance of MyDbContext every time it sees a parameter of type MyDBContext being mentioned in a constructor一旦它知道这一点,它就可以例如在每次看到构造函数中提到的 MyDBContext 类型的参数时创建 MyDbContext 的实例

class WhateverController{
    WhateverController(MyDbContext x) <-- this constructor needs a MyDbContext; the injector will make one and supply it

}

As such, yes the type(or class; same thing) name you provide in the < > is a parameter, telling what type is being registered, but it actually being an instance of the type wouldn't be super helpful because the only thing the injector would reasonably do with it would be to inspect what type the instance is, remember the type and throw away the instance (because it wants to know the type, it doesn't want an instance - it's job is to create instances of the type for you)因此,是的,您在< >中提供的类型(或 class;同样的东西)名称是一个参数,它告诉正在注册的类型,但它实际上是该类型的实例不会非常有用,因为唯一的事情注入器合理地使用它会检查实例是什么类型,记住类型并丢弃实例(因为它想知道类型,它不需要实例 - 它的工作是创建实例为你打字)


When you write a generic method you're essentially giving the compiler a skeleton it can use to write methods for you.当您编写泛型方法时,您实际上是在为编译器提供一个框架,它可以用来为您编写方法。 You specify the method as being generic by putting words in angle brackets and the compiler treats them as types that it can substitute for the types it sees you use when you use the method:您通过将单词放在尖括号中来指定该方法是通用的,编译器将它们视为可以替代它在您使用该方法时看到的类型的类型:

string GetDatedString<T>(T thing){
  return $"{DateTime.Now} {thing.ToString()}"( 
}

If you call this and provide an int the compiler can conceptually write an int flavored version, which means you don't have to write it.如果你调用它并提供一个int编译器可以在概念上编写一个 int 风格的版本,这意味着你不必编写它。

string GetDatedString(int thing){
  return $"{DateTime.Now} {thing.ToString()}"( 
}

Writing one of those for every type in your program would be pretty horrific为程序中的每种类型编写一个这样的程序会非常可怕


There are times, and a good example is when you're setting up dependency injection, when you don't want to (or cannot create) an instance the type you want the injector to provide when it sees you using it.有时,一个很好的例子是当你设置依赖注入时,当你不想(或不能创建)一个你希望注入器在看到你使用它时提供的类型的实例。 Suppose you have some type MyDbContext that implements interface IMyDbContext and you want the DI to inject an instance of MyDbContext every time it sees you using the interface IMyDbContext in a constructor.假设您有一些实现接口 IMyDbContext 的 MyDbContext 类型,并且您希望 DI 每次看到您在构造函数中使用接口 IMyDbContext 时都注入一个 MyDbContext 实例。 If your aim is to communicate the type-you-see and the type-you-shall-create to the injector via some method that took an instance, you'd end up having to cast your type:如果您的目标是通过采用实例的某种方法将您看到的类型和您将创建的类型传达给注入器,那么您最终将不得不强制转换您的类型:

services.AddDbContext((IMyDbContext)new MyDbContext());

Here we could do some trickery to stash the static (compile time) type IMyDbContext as well as the runtime type MyDbContext and then know "whenever i see IMyDbContext provide an instance of MyDbContext" but it's not so pretty and essentially still requires us to provide a useless instance when all the injector wants to know are types.. Being able to pass types as arguments without needing to pass instances is nice and clean and helps out with compile time type related goodness.在这里我们可以做一些技巧来存储 static(编译时)类型 IMyDbContext 以及运行时类型 MyDbContext 然后知道“每当我看到 IMyDbContext 提供 MyDbContext 的实例”但它不是那么漂亮并且基本上仍然需要我们提供一个当所有注入器想知道的都是类型时无用的实例。能够将类型传递为 arguments 而无需传递实例是非常干净的,并且有助于编译时类型相关的优点。 Look at the ugly we had to do to parse an enum before generics:看看我们在 generics 之前解析枚举的丑陋之处:

Status myStatus = (Status)Enum.Parse(typeof(Status), "Active");

Compared to being able to specify a type argument:与能够指定类型参数相比:

Status myStatus = Enum.Parse<Status>("Active");

So all in, with that thing you asked about originally:所以总而言之,关于你最初问的那件事:

services.AddDbContext<MyDbContext>(...)

And another overload:另一个重载:

services.AddDbContext<IMyDbContext, MyDbContext>(...)

that are calls that take arguments that are types because we want it to be - the first is saying "when you come across a class calling for a MyDbContext, give it an instance of one, and the second overload of "when you come across a class calling for a IMyDbContext, give it a MyDbContext" - this letter form being very useful if you want to eg swap your real MyDbContext out for a FakeMyDbContext that also implements IMyDbContext; it looks like a real one for testing purposes but just generates fake data etc so tests run; you don't have to change any of your classes that call for an IMyDbContext, they just call the methods they see in the interface and testing or running your app for real is switched simply by telling the injector "when you see X provide Z instead of Y"这些调用采用 arguments 是类型,因为我们希望它是这样的——第一个是说“当你遇到一个 class 调用 MyDbContext 时,给它一个实例,第二个重载是”当你遇到一个class calling for a IMyDbContext, give it a MyDbContext" - 如果您想将真实的 MyDbContext 换成同样实现 IMyDbContext 的 FakeMyDbContext,这种字母形式非常有用;它看起来像一个用于测试目的的真实的,但只是生成假数据等等,以便测试运行;您不必更改任何调用 IMyDbContext 的类,它们只需调用它们在界面中看到的方法,然后通过告诉注入器“当您看到 X 提供 Z 而不是 Y"

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

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