C# - 接受任何类型的 IEnumerable 作为参数
C# - Accept IEnumerable of any type as parameter
我有这个构造函数,它应该接受任何类型的任何 IEnumerable,并尝试将值转换为所需的类型
public ChartData(IEnumerable<dynamic> labels, IEnumerable<dynamic> values)
{
this.values = values.Select(x=>(float)Convert.ToString(x));
this.labels = labels.Select(x=>(string)Convert.ToString(x));
}
当我调用这个构造函数时,我可以传递字符串类型的 ienumerables,但不能传递 int 或 float
我如何制作一个构造函数来接受任何类型的 ienumerables 而不会产生大量的重载?
如果您接受任何类型,其中还包括 IEnumerable<User>
和 IEnumerable<Car>
,您希望如何将它们转换为浮点数和字符串?拥有一个封装浮点数和字符串值的对象会好得多,为此使用一个接口。例如,有一个这样的界面:
public interface IChartItem
{
float Value { get; set; }
string Label { get; set; }
}
一个例子class:
public class Foo : IChartItem
{
public int MyProperty { get; set; }
public string Name { get; set; }
public float Value => (float)MyProperty;
public string Label => Name;
}
现在您的函数可以是带有约束的通用函数:
public void ChartData<T>(IEnumerable<T> items)
where T : IChartItem
{
this.values = items.Select(x => x.Value);
this.labels = items.Select(x => x.Label);
}
备选方案
您还可以传入您的对象以及几个 Func
参数来获取值和标签。例如:
public void ChartData<T>(IEnumerable<T> items,
Func<T, float> valueSelector,
Func<T, string> labelSelector)
{
this.values = items.Select(x => valueSelector(x));
this.labels = items.Select(x => labelSelector(x));
}
并像这样使用它:
ChartData(foos, f => (float)f.MyProperty, f => f.Name);
如果您不关心类型,请使用非泛型版本 IEnumerable
(这是 IEnumerble<T>
的基本接口):
public ChartData(IEnumerable labels, IEnumerable values)
{
// x is of type Object for both lines.
this.values = values.Select(x=>(float)Convert.ToSingle(x));
this.labels = labels.Select(x=>(string)Convert.ToString(x));
}
注意
- 你失去了所有类型安全性——有人可以轻松地将文件列表或其他一些随机类型作为参数之一传递
- 如果由于装箱成本而处理值类型,一般来说泛型版本更可取。幸运的是,在您的情况下,您仍然需要盒装价值或
Convert
.
我个人只接受 IEnumerable<float>
(或者 double
)和 IEnumerable<String>
作为参数 - 此方法的调用者将有更多知识来选择所需的 fields/conversion比我的代码试图猜测他们传入的类型。如果某些“任何数字类型”将是所需的输入,我会检查 Is there a constraint that restricts my generic method to numeric types? 并实现所有变体,而不是使用 dynamic
回退到运行时反射或非通用版本缺乏类型安全。
我有这个构造函数,它应该接受任何类型的任何 IEnumerable,并尝试将值转换为所需的类型
public ChartData(IEnumerable<dynamic> labels, IEnumerable<dynamic> values)
{
this.values = values.Select(x=>(float)Convert.ToString(x));
this.labels = labels.Select(x=>(string)Convert.ToString(x));
}
当我调用这个构造函数时,我可以传递字符串类型的 ienumerables,但不能传递 int 或 float 我如何制作一个构造函数来接受任何类型的 ienumerables 而不会产生大量的重载?
如果您接受任何类型,其中还包括 IEnumerable<User>
和 IEnumerable<Car>
,您希望如何将它们转换为浮点数和字符串?拥有一个封装浮点数和字符串值的对象会好得多,为此使用一个接口。例如,有一个这样的界面:
public interface IChartItem
{
float Value { get; set; }
string Label { get; set; }
}
一个例子class:
public class Foo : IChartItem
{
public int MyProperty { get; set; }
public string Name { get; set; }
public float Value => (float)MyProperty;
public string Label => Name;
}
现在您的函数可以是带有约束的通用函数:
public void ChartData<T>(IEnumerable<T> items)
where T : IChartItem
{
this.values = items.Select(x => x.Value);
this.labels = items.Select(x => x.Label);
}
备选方案
您还可以传入您的对象以及几个 Func
参数来获取值和标签。例如:
public void ChartData<T>(IEnumerable<T> items,
Func<T, float> valueSelector,
Func<T, string> labelSelector)
{
this.values = items.Select(x => valueSelector(x));
this.labels = items.Select(x => labelSelector(x));
}
并像这样使用它:
ChartData(foos, f => (float)f.MyProperty, f => f.Name);
如果您不关心类型,请使用非泛型版本 IEnumerable
(这是 IEnumerble<T>
的基本接口):
public ChartData(IEnumerable labels, IEnumerable values)
{
// x is of type Object for both lines.
this.values = values.Select(x=>(float)Convert.ToSingle(x));
this.labels = labels.Select(x=>(string)Convert.ToString(x));
}
注意
- 你失去了所有类型安全性——有人可以轻松地将文件列表或其他一些随机类型作为参数之一传递
- 如果由于装箱成本而处理值类型,一般来说泛型版本更可取。幸运的是,在您的情况下,您仍然需要盒装价值或
Convert
.
我个人只接受 IEnumerable<float>
(或者 double
)和 IEnumerable<String>
作为参数 - 此方法的调用者将有更多知识来选择所需的 fields/conversion比我的代码试图猜测他们传入的类型。如果某些“任何数字类型”将是所需的输入,我会检查 Is there a constraint that restricts my generic method to numeric types? 并实现所有变体,而不是使用 dynamic
回退到运行时反射或非通用版本缺乏类型安全。