强类型整数
作为一个业余爱好项目的思想实验,我一直在想一种方法来确保这种微妙的错误/拼写错误不会发生:
public void MyMethod(int useCaseId)
{
// Do something with the useCaseId
}
public void SomeOtherMethod()
{
int userId = 12;
int useCaseId = 15;
MyMethod(userId); // Ooops! Used the wrong value!
}
这个错误很难找到,因为没有编译时错误,而且你不会甚至不一定在运行时出现异常。你只会得到“意想不到的结果”。
为了以简单的方式解决这个问题,我尝试使用空枚举定义。有效地使用户 ID 成为一种数据类型(而不需要达到类或结构的程度):
public enum UseCaseId { // Empty… }
public enum UserId { // Empty… }
public void MyMethod(UseCaseId useCaseId)
{
// Do something with the useCaseId
}
public void SomeOtherMethod()
{
UserId userId = (UserId)12;
UseCaseId useCaseId = (UseCaseId)15;
MyMethod(userId); // Compile error!!
}
您觉得怎么样?
As a thought experiment on a hobby project, I've been thinking of a way to ensure that this sort of subtle bug/typo doesn’t happen:
public void MyMethod(int useCaseId)
{
// Do something with the useCaseId
}
public void SomeOtherMethod()
{
int userId = 12;
int useCaseId = 15;
MyMethod(userId); // Ooops! Used the wrong value!
}
This bug would be hard to find because there’s no compile-time error, and you wouldn’t necessarily even get an exception at run-time. You’d just get "unexpected results".
To resolve this in a simple way, I’ve experimented with using empty enum definitions. Effectively making a user id a data type (without going quite as far as a class or a struct):
public enum UseCaseId { // Empty… }
public enum UserId { // Empty… }
public void MyMethod(UseCaseId useCaseId)
{
// Do something with the useCaseId
}
public void SomeOtherMethod()
{
UserId userId = (UserId)12;
UseCaseId useCaseId = (UseCaseId)15;
MyMethod(userId); // Compile error!!
}
What d’you think?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
游戏迟到了,但是 FWIW ...codeplex 上的这个项目定义了几个“强类型”标量,例如 Angle 、方位角、距离、纬度、经度、弧度等。实际上,每个都是一个结构体,具有单个标量成员和多个方法/属性/构造函数来“正确”操作它。除了它们是值类型而不是引用类型这一事实之外,与将它们都作为一个类没有太大区别。虽然我没有使用过该框架,但我可以看到可能使这些类型成为一等公民的价值。
不知道这最终是否是一个好主意,但能够编写这样的代码(类似于您的原始示例)并确保类型安全(和值安全)似乎确实有用:
或
或
看起来像这样如果您的域有很多具有值语义的标量“类型”,并且可能有很多这些类型的实例,则模式将是最有用的。将每个模型建模为具有单个标量的结构可以提供非常紧凑的表示(与使每个模型成为完整的类相比)。虽然实现这种级别的抽象(而不是仅仅使用“裸”标量来表示域值)和离散性可能有点痛苦,但最终的 API 似乎更容易“正确”使用。
Late to the game, but FWIW ... this project on codeplex has defined several "strongly typed" scalars like Angle, Azimuth, Distance, Latitude, Longitude, Radian, etc. Actually, each of these is a struct with a single scalar member and several methods/properties/constructors to manipulate it "correctly". Not really much different than making each of these a class, aside from the fact that these are value types rather than reference types. While I have not used the framework, I can see the value of possibly making these types first class citizens.
Don't know whether it is ultimately a good idea or not, but it does seem useful to be able to write code like this (similar to your original example) with type safety (and value safety) ensured:
or
or
It seems like this pattern would be most useful if your domain has a lot of scalar "types" with value semantics and potentially many instances of these types. Modeling each as a struct with a single scalar gives a very compact representation (compared making each a full blown class). While it might be somewhat of a pain to implement this level of abstraction (rather than just using "naked" scalars to represent domain values) and discreteness, the resultant API seems like it would be much easier to use "correctly".
聚会已经很晚了,但我认为你强类型化这些概念而不是使用原语是正确的。
您可能会发现这个项目很有用:
https://github.com/SteveDunn/Vogen
Very late to the party, but I think you're right to strongly type such concepts instead of using primitives.
You might find this project useful:
https://github.com/SteveDunn/Vogen
如果您在创建新类型来保存
UserId
和UseCaseId
时遇到了麻烦,您几乎可以轻松地将它们制作成简单的类,并使用UserId
和UseCaseId
中的隐式转换运算符。 code>int 为您提供所需的语法:这样,您就可以获得类型安全性,而不必在代码中使用强制转换。
If you're going to the trouble of creating new types to hold
UserId
andUseCaseId
, you could almost as easily make them into simple classes and use an implicit conversion operator fromint
to give you the syntax you want:That way, you get the type safety without having to litter your code with casts.
如果是 Haskell 并且我想这样做,我可能会这样做:
这样,函数将接受
UserId
而不是 Int,并且创建一个UserId
总是显式的,类似于:这类似于 Niall C. 的创建类型来包装 Int 的解决方案。然而,如果每个类型的实现不需要 10 行就好了。
If it were Haskell and I wanted to do this, I might do it like:
This way, functions will accept a
UserId
instead of an Int, and creating aUserId
is always explicit, something like:This is similar to Niall C.'s solution of creating a type to wrap around an Int. However, it'd be nice if it didn't take 10 lines to implement per type.
我想做类似的事情有一段时间了,你的帖子提示我尝试以下操作:
我可以按如下方式使用
我认为传递这种类型的 Id 对象比传递整个域对象更好,因为它使之间的契约更加明确调用者和被调用者。如果您只传递 id,您就知道被调用者没有访问该对象的任何其他属性。
I have wanted to do something similar for a while and your post prompted me to try the following:
which I can use as follows
I think passing this type of Id object is better than passing the entire domain object because it makes the contract more explicit between the caller and the callee. If you pass just the id, you know that the callee is not accessing any other property on the object.
我个人认为没有必要说实话。
开发人员必须正确实现逻辑,不能依赖编译时错误来解决此类错误。
I personally think it is unnecessary to be honest.
It is down to the developer to implement the logic properly and you can not rely on compile time errors for such bugs.