具有T(struct)和Nullable<T>约束的通用类

4

我正在使用 LinQ2SQL 并实现了一个用户控件,使用数据绑定来编辑不同实体的价格。

public partial class PriceControl<TDataSource, TPricesVat, TPriceNet, TPriceGross>
    : ANotifyPropertyXtraUserControl, IPriceControl<TDataSource, TPricesVat, TPriceNet, TPriceGross>
    where TDataSource : class, INotifyPropertyChanging, INotifyPropertyChanged
    where TPricesVat : struct
    where TPriceNet : struct
    where TPriceGross : struct
{
}

我喜欢同时使用decimaldecimal?两种类型,在代码语句中根据需要进行处理。但是,C#不接受decimal?以便这些类型限制。我发现Nullable<T>也是一个struct,但不像struct一样得到完全支持。
是否有可能在类型约束中实现这一点?我估计答案是“不可以”。但或许……
补充说明:删除TPricesVatTPriceNetTPriceGross的限制后,我可以正常编译此代码,但我无法在设计时强制允许的通用类型。
编辑:由于工作原因,我提出了这个问题。但是我很快就离开了,无法以适当的解决方案关闭这个问题。为了尊重这个问答平台,我在这里留下了这个编辑。
感谢所有有用的提及。

有没有真正的理由将那些通用类型参数限制为结构体?我唯一能想到的原因是,您处理该类型的变量时假定它永远不会为空。如果您没有这样做,那么这个约束真的有意义吗? - jmcilhinney
1
为什么需要结构体?你也可以写类似于 where T : decimal, decimal? 而不是 where T : struct - Florian
@jmcilhinney:我试图强制使用标量类型,因为后台有价格计算运行。我认为使用约束来限制标量类型及其可空的对应类型可以减少我的代码检查次数。@thefiloe:不行,Invalid constraint type. A type used as a constraint must be an interface, a non-sealed class or a type parameter. - codekandis
1
@thefiloe,你不能将泛型类型参数限制为特定的“struct”类型。where T: decimal 的意思是,“T 是 decimal 或任何 decimal 的子类型”,但这并没有多大意义,因为结构体不支持继承。约束 where T: U 仅在 U 是类或接口类型时有效。此外,您只能针对一个约束指定一个类型,where T: U, X 是无效的。 - dcastro
我忘了提到,删除 TPricesVatTPriceNetTPriceGross 的限制让我能够正确编译此代码,但我无法强制在设计时使用允许的泛型类型。 - codekandis
显示剩余2条评论
2个回答

1
你的怀疑是正确的,你无法这样做。你需要为每个方法提供一个重载:
public partial class PriceControl<TDataSource, TPricesVat, TPriceNet, TPriceGross>
  : ANotifyPropertyXtraUserControl, IPriceControl<TDataSource, TPricesVat, TPriceNet, TPriceGross>
  where TDataSource : class, INotifyPropertyChanging, INotifyPropertyChanged
  where TPricesVat : struct
  where TPriceNet : struct
  where TPriceGross : struct
{

   public void DoSomething(TPriceNet price) {}
   public void DoSomething(TPriceNet? price)
   {
       if(price.HasValue)
           DoSomething(price.Value);
       else
       {
          //throw exception? log? no-op?
       }
   }
}

但是,就像其他人问的那样,为什么要将这些类型限制在struct中?一个byteSpinWait可以成为有效的TPriceNet吗?


目标不是强制使用“struct”的限制。我知道使用“bool”会导致我的代码崩溃,因为我基本上处理的是“decimal”值。这只是正确约束这些通用类型的开始。如果我只使用方法,那么你的代码是正确的,但我正在使用属性来获取和设置值。实际上,你让我考虑删除这些属性,改用get和set方法。应该会使任何事情更简单。 - codekandis
@ChristianRamelow 如果有两个getter和两个setter(一个可为空,一个不可为空),那么与拥有两个属性完全相同。没有必要替换您的属性。 - dcastro

0
想象一下有一个方法,它接收一个 decimal 参数并返回一个 decimal?,因为它也可以处理 int 和 int?。
public T? DoSomeMath<T>(T value)
   where T: struct
{
   //to return null you can do this
   return default(T);
   //or
   return null;
} 

注意方法声明中的?

在处理结构体和可空类型时,这是您可以做到最好的。

如果我能将约束限制为where T: System.Nullable<T>,那就太好了。

但是这意味着一个悖论:如果TSystem.Nullable<T>,并且我将T作为参数接收,则表示System.Nullable<>内部的T本身将是另一个System.Nullable<T>

当然,如果您已经知道自己将始终使用十进制数,则无需使用通用类型,只需直接使用decimal即可。


这是由数据库设计所迫。改变这个设计会在整个项目中引起太多的副作用。现在改变它太大了。 - codekandis
你可以在方法内部声明一个 System.Nullable<TPriceNet>。 - Luis Filipe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接