我已经创建了一个代码库,其中我非常依赖float
结构。但是浮动可能意味着任何事情。秒,米,弧度,公斤,秒/秒,弧度/秒等。正确的命名很有帮助,但仍然很容易混淆。因此,我开始为每个单元编写包装浮点数的结构。但是编写这样的结构变得越来越麻烦:
最简单的生成这种结构的方法是什么,这样我就不必维护太多代码了?
奖金问题:通过这种方式,我仍然能够添加一些特殊的运算符。例如,我想添加一个在Meters
中变成Seconds
/ MetersPerSecond
的运算符。
编辑:我在.Net Framework 4.7和C#语言级别7.3
这是我的示例Seconds
结构。如您所见,Meters结构看起来完全一样。
public struct Seconds : IEquatable<Seconds>
{
public Seconds(float value)
{
this.Value = value;
}
public float Value { get; }
public static implicit operator Seconds(float value) => new Seconds(value);
public static implicit operator Seconds(TimeSpan value) => new Seconds((float)value.TotalSeconds);
public static Seconds operator +(Seconds a, Seconds b) => new Seconds(a.Value + b.Value);
public static Seconds operator -(Seconds a, Seconds b) => new Seconds(a.Value - b.Value);
public static bool operator >(Seconds a, Seconds b) => a.Value > b.Value;
public static bool operator <(Seconds a, Seconds b) => a.Value < b.Value;
public static bool operator >=(Seconds a, Seconds b) => a.Value >= b.Value;
public static bool operator <=(Seconds a, Seconds b) => a.Value <= b.Value;
public static bool operator ==(Seconds a, Seconds b) => a.Equals(b);
public static bool operator !=(Seconds a, Seconds b) => !a.Equals(b);
public override int GetHashCode() => this.Value.GetHashCode();
public override bool Equals(object obj)
{
if (obj is Seconds)
{
return this.Equals((Seconds)obj);
}
return false;
}
public bool Equals(Seconds other) => other.Value == this.Value;
public override string ToString() => $"{this.Value.ToString("F2", CultureInfo.InvariantCulture)}s";
}
答案 0 :(得分:1)
尝试使用F#中的Units of Measure功能。它在编译时强制执行度量单位,不影响性能。如果公共表面保持简单,则可以很容易地从C#中使用F#进行组装。拍到浮动对象上的计量单位将编译为IL中的System.Double。
就奖金而言,计量单位由编译器维护和验证。如果将米除以秒,结果将以每秒米为单位。
答案 1 :(得分:1)
如果您想使用C#,可以使用一个简单的Yellicode模板来生成样板代码(使用一些TypeScript)。这是一个生成部分结构(struct-types.template.ts
)的模板。
import { Generator, TextWriter } from '@yellicode/templating';
import { CSharpWriter } from '@yellicode/csharp';
var structNames = ['Seconds', 'Meters']; // add more names here
Generator.generate({ outputFile: './structs.cs' }, (output: TextWriter) => {
var csharp = new CSharpWriter(output);
structNames.forEach(structName => {
const struct: StructDefinition = { name: structName, accessModifier: 'public', isPartial: true, implements: [`IEquatable<${structName}>`] } as StructDefinition;
csharp.writeStructBlock(struct, () => {
// Ctor
csharp.writeMethodBlock({ isConstructor: true, name: structName, accessModifier: 'public', parameters: [{ typeName: 'float', name: 'value' }] }, () => {
csharp.writeLine('this.Value = value;');
});
csharp.writeLine();
// Value
csharp.writeAutoProperty({ typeName: 'float', name: 'value', accessModifier: 'public', hasGetter: true });
csharp.writeLine();
// Operators
csharp.writeLine(`public static implicit operator ${structName}(float value) => new ${structName}(value);`);
csharp.writeLine();
// ... other operators omitted for brevity, type-specific conversion operators would be in a partial file
// Methods
csharp.writeLine('public override int GetHashCode() => this.Value.GetHashCode();');
csharp.writeLine();
// ... other methods omitted for brevity
})
csharp.writeLine();
})
});
配置文件(codegenconfig.json
)应该如下所示:
{
"compileTypeScript": true,
"templates": [
{
"templateFile": "struct-types.template.ts"
}
]
}
快速入门(确保已安装NPM,然后打开命令提示符):
npm install @yellicode/cli -g
npm init -y
npm install --save-dev @yellicode/templating @yellicode/csharp
yellicode --watch
生成代码。如果要为每个结构使用单独的输出文件,请从structNames.forEach(..
循环开始,并为每个结构调用Generator.generate(...
。