简体   繁体   English

C#类实例属性机制

[英]C# class instance attribute mechanism

Is there a sane way in C# to achieve the following construct (in pseudocode): C#中是否有一种明智的方法来实现以下构造(以伪代码形式):

void Method(MyClass<Attribute1, Attribute2, ...> foo)
{
  // here I am guaranteed that foo has the specified attributes
}

Where Attribute* are, for example, enum values, such that only instances of MyClass instantiated with the attributes required by the method can be passed to the method (and otherwise fail to compile)? 例如,在Attribute*是枚举值的情况下,只有用该方法所需的属性实例化的MyClass实例才能传递给该方法(否则无法编译)?

I tried looking at generics since I know that C++ templates can make this work so it seemed like a logical starting point, but I couldn't get it working elegantly (I tried using interfaces to constrain the types of the parameter in this fashion but it was very bulky and frankly unusable since I have at least 4 attributes). 我尝试查看泛型,因为我知道C ++模板可以完成此工作,因此这似乎是一个合理的起点,但是我无法使其优雅地工作(我尝试使用接口以这种方式约束参数的类型,但是由于我至少有4个属性,因此体积非常大,并且坦率地说无法使用)。

I want to do this to avoid having lots of annoying checks at the beginning of each method. 我这样做是为了避免在每种方法开始时进行许多烦人的检查。 I am doing DirectX 11 graphics development so I am kind of constrained by the API which does not make it particularly easy to pass objects around in this "type-safe" manner (in DirectX every resource has a large "Description" structure which contains information about what the resource can and cannot do, is and is not, etc.. and is tedious and error-prone to parse, so I am trying to write a wrapper around it for my and my users' convenience). 我正在进行DirectX 11图形开发,因此受到API的限制,该API并不特别容易以这种“类型安全”的方式传递对象(在DirectX中,每个资源都具有包含信息的大型“描述”结构。关于资源可以做什么和不能做什么,是和不是的等等。并且解析起来很繁琐且容易出错,因此为了我和我的用户的方便,我试图围绕它编写一个包装器。

I also cannot have different class types for every case because there are a lot of combinations, so this seems like the most comfortable way to write code like this, and I am hoping C# makes this possible. 我也不能在每种情况下都有不同的类类型,因为有很多组合,所以这似乎是编写这样的代码的最舒适的方式,我希望C#能够做到这一点。


I'm sure there is a name for this kind of language feature (if you know it please let me know, I would have googled but this is kind of hard to search for when you don't know the proper keywords...) 我敢肯定这种语言功能有一个名字(如果知道的话,请告诉我,我会用Google搜索的,但是当您不知道合适的关键字时,这种搜索就很难了...)

Generic type parameters in .NET must be types themselves. .NET中的通用类型参数必须是类型本身。 You can't create a generic type/method that is specific to a particular value of the Generic type parameter only. 您不能创建仅特定于通用类型参数的特定值的通用类型/方法。

If you do not want or cannot create a type that represents the attribute values you want your method being restricted to, you will have to do sanity checks in your method to ensure that the proper attribute values are used in the provided "foo" object. 如果您不希望或无法创建表示要限制方法使用的属性值的类型,则必须对方法进行完整性检查,以确保在提供的“ foo”对象中使用了正确的属性值。


Using specific types as representation of specific attribute values might be an answer for the problem you asked about, but it has the disadvantage of not supporting switch-case statements (see further below). 使用特定类型表示特定属性值可能是您所问问题的答案,但是它的缺点是不支持switch-case语句(请参见下文)。 Please also read the final note at the end of my answer. 也请在我的答案末尾阅读最终说明。

Say, you want a type that represents textures. 假设您想要一个代表纹理的类型。 Textures can have different number of channels, and different bit depths. 纹理可以具有不同数量的通道和不同的位深度。 You could then declare a generic texture type like this: 然后可以声明一个通用的纹理类型,如下所示:

class Texture<TChannels, TBPC>
    where TChannels : INumOfChannels,new()
    where TBPC : IBitsPerChannel,new()

INumOfChannels and IBitsPerChannel are just interfaces and can be empty. INumOfChannelsIBitsPerChannel只是接口,可以为空。
The new() constraint prevents creation of a concrete Texture type by using the interfaces themselves. new()约束阻止使用接口本身来创建具体的Texture类型。

For different channels and different BPCs, you will create empty types extending from the respective base interfaces, for example: 对于不同的通道和不同的BPC,您将创建从相应基本接口扩展的空类型,例如:

class FourChannels : INumOfChannels {};
class ThreeChannels : INumOfChannels {};

class BitsPerChannel_24 : IBitsPerChannel {};
class BitsPerChannel_32 : IBitsPerChannel {};

Using this, you can restrict your generic method to certain attribute combinations. 使用此方法,可以将通用方法限制为某些属性组合。 In case your method should only deal with 4-channel and 32bpc textures: 如果您的方法只能处理4通道和32bpc纹理:

void MyMethod<TChannels, TBPC>(Texture<TChannels, TBPC> foo)
    where TChannels : FourChannels
    where TBPC : BitsPerChannel_32


Now, every good thing also has dark sides. 现在,每件好事也都有阴暗面。 How would you do something like this (written as pseudo-code)? 您将如何做这样的事情(写为伪代码)?

switch (NumOfChannelsAttribute)
{
    case FourChannels:
        // do something
        break;

    case ThreeChannels:
        // do something else
        break;
}

You can't, at least not in an easy and simple way, because "FourChannel" and "ThreeChannel" are types, not integral values. 至少不能以一种简单的方式,因为“ FourChannel”和“ ThreeChannel”是类型,而不是整数值。

Of course, you can still use if constructs. 当然,您仍然可以使用if构造。 For this to work you would need to implement a property in the generic texture type which provides the used attributes: 为此,您需要在通用纹理类型中实现一个属性,该属性提供使用的属性:

class Texture<TChannels, TBPC> where TChannels : INumOfChannels,new() where TBPC : IBitsPerChannel,new()
{
    public Type ChannelsAttribute
    {
        get { return typeof(TChannels); }
    }

    public Type BitsPerChannelAttribute
    {
        get { return typeof(TBPC); }
    }
}

In an if construct, you could utilize this as follows: if构造中,您可以如下利用它:

var myTex = new Texture<FourChannels, BitsPerChannel_32>();

if (myTex.ChannelsAttribute == typeof(FourChannels))
{
    ... do something with 4 channels
}
else
{
    ... though luck, only 4 channels are supported...
}


A final note and advice: 最后的注意事项和建议:
While this might work for your problem, resorting to these kind of 'tricks' usually is an indication of a flawed design. 尽管这可能会解决您的问题,但诉诸此类“技巧”通常表明设计存在缺陷。 I think it is well-invested time if you revisit the design choices you made in your code, so you don't need to rely on crutches like this. 我认为,如果您重新查看在代码中所做的设计选择,这将是一个投入充分的时间,因此您无需依赖这样的拐杖。

C# doesn't have such a feature. C#没有这种功能。 You mention you have tried using interfaces, but don't specify how. 您提到您尝试使用接口,但未指定如何使用。 The way I'd suggest you try using them is by using generics with multiple constraints, eg 我建议您尝试使用它们的方式是使用具有多个约束的泛型,例如

void Method(T foo) where T : IAttribute1, IAttribute2, IAttribute3, IAttribute4
{
}

Let's say one such attribute class is then ICpuMappable, then you can constrain types that can be used with Method1 with: 假设一个这样的属性类是ICpuMappable,然后可以使用以下方法约束可以与Method1一起使用的类型:

void Method1(T foo) where T : ICpuMappable
{
}

and you can know any foo passed to Method1 is CPU mappable. 并且您可以知道传递给Method1所有foo都是CPU可映射的。

You'll likely end up with lots of interfaces, but as many will be treated as "flags", they shouldn't be too difficult to maintain. 您可能最终会拥有很多接口,但是由于许多接口都将被视为“标志”,因此它们的维护应该不会太困难。

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

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