将 属性 轻松转换为多种类型的 C# 最佳实践是什么?
What's a C# best practice for having a property be easily cast to multiple types?
我正在编写 C# class。例如,class 是一个距离 class,用于跟踪以米为单位的距离 float
值,同时还具有针对不同单位的多个属性。比如厘米,公里等等
我希望能够使这些属性可以隐式地代替数字 (float
) 以及 string
来使用。意思是,我希望能够 distance.centimeters + 1
计算 1 加上以厘米为单位的距离,并在其他地方使用它,我还希望能够 Console.WriteLine(distance.centimeters)
打印自定义字符串,比如附加单位(例如 104 cm
如果 meters
属性 的值为 1.04
)。
在研究过程中,我发现您可以进行自定义隐式类型转换,但这适用于 class/object 级别,但不适用于 属性(即 float
)。
所以现在我想创建:
- 不同于浮点数 属性 的字符串 属性(名称末尾有“String”)
- 或者 returns 字符串的方法(名称末尾有“ToString()”,类似于被覆盖的“.ToString()”方法)。
关于此问题的最佳做法是什么?我可能需要考虑的这两个选项之间有什么区别?我将不胜感激与该主题相关的所有想法。
示例代码:
public class Distance
{
public float meters;
public Distance (float m)
{
meters = m;
}
public float centimeters
{
get
{
return meters * 100;
}
}
// Option 1
public string centimetersString
{
get
{
return centimeters + " cm";
}
}
// Option 2
public string centimetersToString()
{
return centimeters + " cm";
}
}
我为你做了一些东西,我希望它能帮助你,满足你的需求。
默认值始终为厘米。但是你当然可以随意更改它。
public class Distance
{
public float Value { get; set; }
public Distance(float value, MetricSystem metric)
{
Value = metric switch
{
MetricSystem.Centimeter => value,
MetricSystem.Meter => value * 100,
_ => Value
};
}
public void AddValue(float value, MetricSystem metric)
{
Value += metric switch
{
MetricSystem.Centimeter => value,
MetricSystem.Meter => value * 100,
_ => 0
};
}
public string ReadValue(MetricSystem metric)
{
return metric switch
{
MetricSystem.Centimeter => $"{Value} cm",
MetricSystem.Meter => $"{Value / 100} m",
_ => string.Empty
};
}
}
public enum MetricSystem
{
Centimeter,
Meter
}
我的结果是:
您可以将它们分解成不同的 classes。每种测量类型一个。这样你就有了一个专用于特定类型(比如厘米)的 class,并且你可以在 class 到 return 中有不同的属性,你的值以多种格式格式化。原则是你可能试图在一个 class 中做太多事情。这是一些示例代码。这可能有多种变体,例如抽象基础 class 等。这是“A”解决方案,通过拆分为多个 classes 来向您展示我的意思。
代码来自 LinqPad。
void Main()
{
var dc = new Distance(10);
dc.ToString().Dump();
var cent = dc.ToCentimeters();
cent.Centimeters.Dump();
cent.ToString().Dump();
var mil = dc.ToMillimeters();
mil.Millimeters.Dump();
mil.ToString().Dump();
// outputs
//10 Meters
//1000
//1000 Centimeters
//10000
//10000 Millimeters
}
// You can define other methods, fields, classes and namespaces here
public class Distance
{
private float _meters;
public Distance (float meters)
{
_meters = meters;
}
public DistanceCentimeters ToCentimeters(){
return new DistanceCentimeters(_meters * 100);
}
public DistanceMillimeters ToMillimeters(){
return new DistanceMillimeters(_meters * 1000);
}
public override string ToString(){
return $"{_meters} Meters";
}
}
public class DistanceCentimeters{
public DistanceCentimeters(float centimeters){
_centimeters = centimeters;
}
private float _centimeters;
public float Centimeters
{
get
{
return _centimeters;
}
}
public override string ToString(){
return $"{Centimeters} Centimeters";
}
}
public class DistanceMillimeters {
public DistanceMillimeters(float millimeters){
_millimeters = millimeters;
}
private float _millimeters;
public float Millimeters
{
get
{
return _millimeters;
}
}
public override string ToString(){
return $"{Millimeters} Millimeters";
}
}
您可以将每个距离类型封装在自己的 class 中,然后使用 implicit operator
提供转换。
这种方法的缺点是每个距离 class 必须包含 implicit operator
实现以将所有其他距离类型转换为其类型。
话虽如此,这里有一个例子可以说明我的意思:
using System;
namespace Demo
{
static class Program
{
static void Main()
{
Metres metres = 10;
Centimetres centimetres = metres;
Console.WriteLine(centimetres); // Prints "1000cm"
metres = centimetres;
Console.WriteLine(metres); // Prints "10m"
var kilometres = (Kilometres) centimetres;
Console.WriteLine(kilometres); // Prints "0.01km"
}
}
public sealed class Metres
{
public Metres(float metres)
{
_metres = metres;
}
public float Distance => _metres;
public static implicit operator Metres(Centimetres centimetres) => new (centimetres.Distance / 100.0f);
public static implicit operator Metres(Kilometres kilometres) => new (kilometres.Distance / 100_000.0f);
public static implicit operator Metres(float metres) => new (metres);
public override string ToString()
{
return $"{_metres}m";
}
readonly float _metres;
}
public sealed class Centimetres
{
public Centimetres(float centimetres)
{
_centimetres = centimetres;
}
public float Distance => _centimetres;
public static implicit operator Centimetres(Metres metres) => new (metres.Distance * 100.0f);
public static implicit operator Centimetres(Kilometres kilometres) => new (kilometres.Distance * 100_000.0f);
public static implicit operator Centimetres(float centimetres) => new (centimetres);
public override string ToString()
{
return $"{_centimetres}cm";
}
readonly float _centimetres;
}
public sealed class Kilometres
{
public Kilometres(float kilometres)
{
_kilometres = kilometres;
}
public float Distance => _kilometres;
public static implicit operator Kilometres(Metres metres) => new (metres.Distance / 1000.0f);
public static implicit operator Kilometres(Centimetres centimetres) => new (centimetres.Distance /100_000.0f);
public static implicit operator Kilometres(float kilometres) => new (kilometres);
public override string ToString()
{
return $"{_kilometres}km";
}
readonly float _kilometres;
}
}
.net 上的可运行示例 Fiddle:https://dotnetfiddle.net/tJE62S
如果你想添加一些重载的算术运算符,它会涉及更多:
using System;
namespace Demo
{
static class Program
{
static void Main()
{
Metres metres = 10;
Centimetres centimetres = metres;
Console.WriteLine(centimetres); // Prints "1000cm"
metres = centimetres;
Console.WriteLine(metres); // Prints "10m"
var kilometres = (Kilometres) centimetres;
Console.WriteLine(kilometres); // Prints "0.01km"
var addedMetres = metres + 10;
Console.WriteLine(addedMetres); // Prints "20m"
var subtractedCm = centimetres - 100;
Console.WriteLine(subtractedCm); // Prints "900cm"
// This would be an ambiguous call - should the result be Centimetres or Metres?
// var difference = addedMetres - subtractedCm;
// So fix it by casting one of the operands to the result type that you want:
var diffCm = (Centimetres)addedMetres - subtractedCm;
Console.WriteLine(diffCm); // Prints "1100cm"
var diffM = addedMetres - (Metres)subtractedCm;
Console.WriteLine(diffM); // Prints "11m"
}
}
public sealed class Metres
{
public Metres(float metres)
{
_metres = metres;
}
public float Distance => _metres;
public static implicit operator Metres(Centimetres centimetres) => new (centimetres.Distance / 100.0f);
public static implicit operator Metres(Kilometres kilometres) => new (kilometres.Distance / 100_000.0f);
public static implicit operator Metres(float metres) => new (metres);
public static Metres operator +(Metres a, Metres b) => new (a.Distance + b.Distance);
public static Metres operator -(Metres a, Metres b) => new (a.Distance - b.Distance);
public override string ToString()
{
return $"{_metres}m";
}
readonly float _metres;
}
public sealed class Centimetres
{
public Centimetres(float centimetres)
{
_centimetres = centimetres;
}
public float Distance => _centimetres;
public static implicit operator Centimetres(Metres metres) => new (metres.Distance * 100.0f);
public static implicit operator Centimetres(Kilometres kilometres) => new (kilometres.Distance * 100_000.0f);
public static implicit operator Centimetres(float centimetres) => new (centimetres);
public static Centimetres operator +(Centimetres a, Centimetres b) => new (a.Distance + b.Distance);
public static Centimetres operator -(Centimetres a, Centimetres b) => new (a.Distance - b.Distance);
public override string ToString()
{
return $"{_centimetres}cm";
}
readonly float _centimetres;
}
public sealed class Kilometres
{
public Kilometres(float kilometres)
{
_kilometres = kilometres;
}
public float Distance => _kilometres;
public static implicit operator Kilometres(Metres metres) => new (metres.Distance / 1000.0f);
public static implicit operator Kilometres(Centimetres centimetres) => new (centimetres.Distance /100_000.0f);
public static implicit operator Kilometres(float kilometres) => new (kilometres);
public static Kilometres operator +(Kilometres a, Kilometres b) => new (a.Distance + b.Distance);
public static Kilometres operator -(Kilometres a, Kilometres b) => new (a.Distance - b.Distance);
public override string ToString()
{
return $"{_kilometres}km";
}
readonly float _kilometres;
}
}
.net 上的可运行示例 Fiddle:https://dotnetfiddle.net/uoN1Wr
看完所有答案后,我选择接受Matthew Watson的答案,因为这是最接近我想做的事情。这是我的代码看起来像是受他的回答启发的样子。如果我需要额外的度量前缀,我只需将 To<InsertPrefix>()
添加到接口,在每个 class 中实现它,并为其创建一个 class 并相应地实现方法。
public interface IDistance
{
public double Value();
public string ToString();
public Centimeters ToCentimeters();
public Meters ToMeters();
}
public class Meters : IDistance
{
private double _meters;
public Meters(double m)
{
_meters = m;
}
public Meters(IDistance distance)
{
_meters = distance.ToMeters()._meters;
}
public double Value()
{
return _meters;
}
public override string ToString() => $"{_meters} m";
public Centimeters ToCentimeters() => new Centimeters(_meters * 100);
public Meters ToMeters() => this;
public static Meters operator +(Meters m, IDistance d) => new Meters(m.Value() + d.ToMeters().Value());
public static Meters operator +(IDistance d, Meters m) => new Meters(m.Value() + d.ToMeters().Value());
}
public class Centimeters : IDistance
{
private double _centimeters;
public Centimeters(double c)
{
_centimeters = c;
}
public Centimeters(IDistance distance)
{
_centimeters = distance.ToCentimeters()._centimeters;
}
public double Value()
{
return _centimeters;
}
public override string ToString() => $"{_centimeters} cm";
public Centimeters ToCentimeters() => this;
public Meters ToMeters() => new Meters(_centimeters / 100);
public static Centimeters operator +(Centimeters c, IDistance d) => new Centimeters(c.Value() + d.ToCentimeters().Value());
public static Centimeters operator +(IDistance d, Centimeters c) => new Centimeters(c.Value() + d.ToCentimeters().Value());
}
用法
public void Test()
{
IDistance distance = new Centimeters(4);
distance = distance + new Meters(4);
Trace.WriteLine(distance.ToMeters().ToString()); // "1.04 m"
}
我正在编写 C# class。例如,class 是一个距离 class,用于跟踪以米为单位的距离 float
值,同时还具有针对不同单位的多个属性。比如厘米,公里等等
我希望能够使这些属性可以隐式地代替数字 (float
) 以及 string
来使用。意思是,我希望能够 distance.centimeters + 1
计算 1 加上以厘米为单位的距离,并在其他地方使用它,我还希望能够 Console.WriteLine(distance.centimeters)
打印自定义字符串,比如附加单位(例如 104 cm
如果 meters
属性 的值为 1.04
)。
在研究过程中,我发现您可以进行自定义隐式类型转换,但这适用于 class/object 级别,但不适用于 属性(即 float
)。
所以现在我想创建:
- 不同于浮点数 属性 的字符串 属性(名称末尾有“String”)
- 或者 returns 字符串的方法(名称末尾有“ToString()”,类似于被覆盖的“.ToString()”方法)。
关于此问题的最佳做法是什么?我可能需要考虑的这两个选项之间有什么区别?我将不胜感激与该主题相关的所有想法。
示例代码:
public class Distance
{
public float meters;
public Distance (float m)
{
meters = m;
}
public float centimeters
{
get
{
return meters * 100;
}
}
// Option 1
public string centimetersString
{
get
{
return centimeters + " cm";
}
}
// Option 2
public string centimetersToString()
{
return centimeters + " cm";
}
}
我为你做了一些东西,我希望它能帮助你,满足你的需求。 默认值始终为厘米。但是你当然可以随意更改它。
public class Distance
{
public float Value { get; set; }
public Distance(float value, MetricSystem metric)
{
Value = metric switch
{
MetricSystem.Centimeter => value,
MetricSystem.Meter => value * 100,
_ => Value
};
}
public void AddValue(float value, MetricSystem metric)
{
Value += metric switch
{
MetricSystem.Centimeter => value,
MetricSystem.Meter => value * 100,
_ => 0
};
}
public string ReadValue(MetricSystem metric)
{
return metric switch
{
MetricSystem.Centimeter => $"{Value} cm",
MetricSystem.Meter => $"{Value / 100} m",
_ => string.Empty
};
}
}
public enum MetricSystem
{
Centimeter,
Meter
}
我的结果是:
您可以将它们分解成不同的 classes。每种测量类型一个。这样你就有了一个专用于特定类型(比如厘米)的 class,并且你可以在 class 到 return 中有不同的属性,你的值以多种格式格式化。原则是你可能试图在一个 class 中做太多事情。这是一些示例代码。这可能有多种变体,例如抽象基础 class 等。这是“A”解决方案,通过拆分为多个 classes 来向您展示我的意思。
代码来自 LinqPad。
void Main()
{
var dc = new Distance(10);
dc.ToString().Dump();
var cent = dc.ToCentimeters();
cent.Centimeters.Dump();
cent.ToString().Dump();
var mil = dc.ToMillimeters();
mil.Millimeters.Dump();
mil.ToString().Dump();
// outputs
//10 Meters
//1000
//1000 Centimeters
//10000
//10000 Millimeters
}
// You can define other methods, fields, classes and namespaces here
public class Distance
{
private float _meters;
public Distance (float meters)
{
_meters = meters;
}
public DistanceCentimeters ToCentimeters(){
return new DistanceCentimeters(_meters * 100);
}
public DistanceMillimeters ToMillimeters(){
return new DistanceMillimeters(_meters * 1000);
}
public override string ToString(){
return $"{_meters} Meters";
}
}
public class DistanceCentimeters{
public DistanceCentimeters(float centimeters){
_centimeters = centimeters;
}
private float _centimeters;
public float Centimeters
{
get
{
return _centimeters;
}
}
public override string ToString(){
return $"{Centimeters} Centimeters";
}
}
public class DistanceMillimeters {
public DistanceMillimeters(float millimeters){
_millimeters = millimeters;
}
private float _millimeters;
public float Millimeters
{
get
{
return _millimeters;
}
}
public override string ToString(){
return $"{Millimeters} Millimeters";
}
}
您可以将每个距离类型封装在自己的 class 中,然后使用 implicit operator
提供转换。
这种方法的缺点是每个距离 class 必须包含 implicit operator
实现以将所有其他距离类型转换为其类型。
话虽如此,这里有一个例子可以说明我的意思:
using System;
namespace Demo
{
static class Program
{
static void Main()
{
Metres metres = 10;
Centimetres centimetres = metres;
Console.WriteLine(centimetres); // Prints "1000cm"
metres = centimetres;
Console.WriteLine(metres); // Prints "10m"
var kilometres = (Kilometres) centimetres;
Console.WriteLine(kilometres); // Prints "0.01km"
}
}
public sealed class Metres
{
public Metres(float metres)
{
_metres = metres;
}
public float Distance => _metres;
public static implicit operator Metres(Centimetres centimetres) => new (centimetres.Distance / 100.0f);
public static implicit operator Metres(Kilometres kilometres) => new (kilometres.Distance / 100_000.0f);
public static implicit operator Metres(float metres) => new (metres);
public override string ToString()
{
return $"{_metres}m";
}
readonly float _metres;
}
public sealed class Centimetres
{
public Centimetres(float centimetres)
{
_centimetres = centimetres;
}
public float Distance => _centimetres;
public static implicit operator Centimetres(Metres metres) => new (metres.Distance * 100.0f);
public static implicit operator Centimetres(Kilometres kilometres) => new (kilometres.Distance * 100_000.0f);
public static implicit operator Centimetres(float centimetres) => new (centimetres);
public override string ToString()
{
return $"{_centimetres}cm";
}
readonly float _centimetres;
}
public sealed class Kilometres
{
public Kilometres(float kilometres)
{
_kilometres = kilometres;
}
public float Distance => _kilometres;
public static implicit operator Kilometres(Metres metres) => new (metres.Distance / 1000.0f);
public static implicit operator Kilometres(Centimetres centimetres) => new (centimetres.Distance /100_000.0f);
public static implicit operator Kilometres(float kilometres) => new (kilometres);
public override string ToString()
{
return $"{_kilometres}km";
}
readonly float _kilometres;
}
}
.net 上的可运行示例 Fiddle:https://dotnetfiddle.net/tJE62S
如果你想添加一些重载的算术运算符,它会涉及更多:
using System;
namespace Demo
{
static class Program
{
static void Main()
{
Metres metres = 10;
Centimetres centimetres = metres;
Console.WriteLine(centimetres); // Prints "1000cm"
metres = centimetres;
Console.WriteLine(metres); // Prints "10m"
var kilometres = (Kilometres) centimetres;
Console.WriteLine(kilometres); // Prints "0.01km"
var addedMetres = metres + 10;
Console.WriteLine(addedMetres); // Prints "20m"
var subtractedCm = centimetres - 100;
Console.WriteLine(subtractedCm); // Prints "900cm"
// This would be an ambiguous call - should the result be Centimetres or Metres?
// var difference = addedMetres - subtractedCm;
// So fix it by casting one of the operands to the result type that you want:
var diffCm = (Centimetres)addedMetres - subtractedCm;
Console.WriteLine(diffCm); // Prints "1100cm"
var diffM = addedMetres - (Metres)subtractedCm;
Console.WriteLine(diffM); // Prints "11m"
}
}
public sealed class Metres
{
public Metres(float metres)
{
_metres = metres;
}
public float Distance => _metres;
public static implicit operator Metres(Centimetres centimetres) => new (centimetres.Distance / 100.0f);
public static implicit operator Metres(Kilometres kilometres) => new (kilometres.Distance / 100_000.0f);
public static implicit operator Metres(float metres) => new (metres);
public static Metres operator +(Metres a, Metres b) => new (a.Distance + b.Distance);
public static Metres operator -(Metres a, Metres b) => new (a.Distance - b.Distance);
public override string ToString()
{
return $"{_metres}m";
}
readonly float _metres;
}
public sealed class Centimetres
{
public Centimetres(float centimetres)
{
_centimetres = centimetres;
}
public float Distance => _centimetres;
public static implicit operator Centimetres(Metres metres) => new (metres.Distance * 100.0f);
public static implicit operator Centimetres(Kilometres kilometres) => new (kilometres.Distance * 100_000.0f);
public static implicit operator Centimetres(float centimetres) => new (centimetres);
public static Centimetres operator +(Centimetres a, Centimetres b) => new (a.Distance + b.Distance);
public static Centimetres operator -(Centimetres a, Centimetres b) => new (a.Distance - b.Distance);
public override string ToString()
{
return $"{_centimetres}cm";
}
readonly float _centimetres;
}
public sealed class Kilometres
{
public Kilometres(float kilometres)
{
_kilometres = kilometres;
}
public float Distance => _kilometres;
public static implicit operator Kilometres(Metres metres) => new (metres.Distance / 1000.0f);
public static implicit operator Kilometres(Centimetres centimetres) => new (centimetres.Distance /100_000.0f);
public static implicit operator Kilometres(float kilometres) => new (kilometres);
public static Kilometres operator +(Kilometres a, Kilometres b) => new (a.Distance + b.Distance);
public static Kilometres operator -(Kilometres a, Kilometres b) => new (a.Distance - b.Distance);
public override string ToString()
{
return $"{_kilometres}km";
}
readonly float _kilometres;
}
}
.net 上的可运行示例 Fiddle:https://dotnetfiddle.net/uoN1Wr
看完所有答案后,我选择接受Matthew Watson的答案,因为这是最接近我想做的事情。这是我的代码看起来像是受他的回答启发的样子。如果我需要额外的度量前缀,我只需将 To<InsertPrefix>()
添加到接口,在每个 class 中实现它,并为其创建一个 class 并相应地实现方法。
public interface IDistance
{
public double Value();
public string ToString();
public Centimeters ToCentimeters();
public Meters ToMeters();
}
public class Meters : IDistance
{
private double _meters;
public Meters(double m)
{
_meters = m;
}
public Meters(IDistance distance)
{
_meters = distance.ToMeters()._meters;
}
public double Value()
{
return _meters;
}
public override string ToString() => $"{_meters} m";
public Centimeters ToCentimeters() => new Centimeters(_meters * 100);
public Meters ToMeters() => this;
public static Meters operator +(Meters m, IDistance d) => new Meters(m.Value() + d.ToMeters().Value());
public static Meters operator +(IDistance d, Meters m) => new Meters(m.Value() + d.ToMeters().Value());
}
public class Centimeters : IDistance
{
private double _centimeters;
public Centimeters(double c)
{
_centimeters = c;
}
public Centimeters(IDistance distance)
{
_centimeters = distance.ToCentimeters()._centimeters;
}
public double Value()
{
return _centimeters;
}
public override string ToString() => $"{_centimeters} cm";
public Centimeters ToCentimeters() => this;
public Meters ToMeters() => new Meters(_centimeters / 100);
public static Centimeters operator +(Centimeters c, IDistance d) => new Centimeters(c.Value() + d.ToCentimeters().Value());
public static Centimeters operator +(IDistance d, Centimeters c) => new Centimeters(c.Value() + d.ToCentimeters().Value());
}
用法
public void Test()
{
IDistance distance = new Centimeters(4);
distance = distance + new Meters(4);
Trace.WriteLine(distance.ToMeters().ToString()); // "1.04 m"
}