情况
我的类在内部处理许多不同类型的文件路径:一些是本地的,一些是远程的;一些亲戚,一些绝对。
过去,它的许多方法都将它们作为string
传递给彼此,但很难跟踪每个方法所期望的确切路径类型。
所需的修复
因此,我们基本上希望typedef
四种不同类型string
:RemoteRelative
,LocalRelative
,RemoteAbsolute
和LocalAbsolute
。这样,静态类型检查器可以帮助开发人员确保他们提供并期望string
具有正确的语义。
不幸的是,string
在BCL中是sealed
,因此我们无法通过简单继承来执行此操作。而且没有简单的typedef
,所以我们也不能这样做。
实际修复
我最终创建了四个不同的简单类,每个类都包含readonly string
。
public struct LocalAbsolutePath {
public readonly string path;
public LocalAbsolutePath(string path) {
this.path = path;
}
}
这大部分都有效,但最终会增加一些不受欢迎的冗长。
问题:我是否忽略了任何适合简单C#语法的替代方案?
就像我上面提到的,C风格的typedef string LocalAbsolutePath;
甚至是F#风格的type LocalAbsolutePath = string
都是我的梦想。但即使是自定义课程方向迈出的一步也会很棒。
答案 0 :(得分:14)
你的解决方案很好。您可以通过向string
添加类型转换来对抗额外的详细程度,让您在LocalAbsolutePath
可以使用的任何地方使用string
。
public struct LocalAbsolutePath { // Making it a class would be OK too
private readonly string path; // <<=== It is now private
public LocalAbsolutePath(string path) {
this.path = path;
}
public static implicit operator string(LocalAbsolutePath p) {
return p.path;
}
}
答案 1 :(得分:5)
你应该做的是保持你当前的方法为你的路径类型创建四个不同的类(甚至让它们继承相同的基类),这样你就可以限制方法只接收这四个Path
中的一个对象。
虽然我不认为var myPath = new LocalAbsolutePath("path")
真的比var myPath = "path"
更加冗长,但由于它缺乏简洁性,因此可以明确地弥补,但如果你真的想要,你可以实施你的类和字符串之间的隐式转换运算符,并让它工作:
public static implicit operator LocalAbsolutePath(string path)
{
return new LocalAbsolutePath(path);
}
现在你可以这样做:
LocalAbsolutePath myPath = "Path String";
答案 2 :(得分:0)
由于我在一个正在进行的项目中设定了相同的目标,并且我从这里的答案中获益匪浅,我想我会分享我最终得到的解决方案。处理null,特别是在单元测试断言中,几乎让我疯狂。以下将失败:
string someStringVar = null;
MyStringType myStringType = new MyStringType(someStringVar);
MyStringType myStringTypeNull = null;
Assert.AreEqual(myStringType, myStringTypeNull);
使用静态Parse()代替公共构造函数更令人满意,因为它让我返回null。通过了:
string someStringVar = null;
MyStringType myStringType = MyStringType.Parse(someStringVar);
MyStringType myStringTypeNull = null;
Assert.AreEqual(myStringType, myStringTypeNull);
另外,我不希望从字符串到MyStringType的隐式转换 - 在我看来,首先要删除一些有意识的编码器好处。允许从字符串进行隐式转换意味着对具有MyStringType参数的方法的调用将接受字符串,并且我不希望这样,因为具有大量字符串参数的方法很容易出错。隐式的逆向转换对我来说更有意义。
最后,我认为这是一个易于重复使用的通用的理想案例。
无论如何,这就是我最终的结果:
public class StringType<T> where T:class
{
private readonly string _str;
protected StringType(string str)
{
_str = str;
}
public static implicit operator string(StringType<T> obj)
{
return obj == null ? null : obj._str;
}
public override string ToString()
{
return _str;
}
public override int GetHashCode()
{
return _str.GetHashCode();
}
}
public class MyStringType : StringType<MyStringType>
{
protected MyStringType(string str) : base(str) { }
public static MyStringType Parse(object obj)
{
var str = obj is string ? (string)obj : (obj == null ? null : obj.ToString());
return str == null ? null : new MyStringType(str);
}
}
评论/改进/简化当然欢迎!
答案 3 :(得分:-2)
也许我在这里咆哮错误的树,但据我所知,typedef
在C#中被实现为类别别名。设置类型别名就像这样简单:
using LocalAbsolutePath = System.String;
然后您可以开始使用LocalAbsolutePath
作为有效类型。或多或少是这样的:
LocalAbsolutePath thisPath = "c:\\thisPath";
根据你的帖子,我觉得这就是你要找的东西。希望我是对的......!