何时使用泛型和类型检查?
When to use generics and type checking?
假设 A
到 Z
是我定义的 26 类。在以下示例中:
private List<A> _listA;
private List<B> _listB;
// private List<C>, and so on, through...
private List<Z> _listZ;
private void setLabelA()
{
LabelA.Text = _listA.Count;
}
// private void setLabelB() exists
// and so does setLabelC()
// and so on, all the way through to...
private void setLabelZ()
{
LabelA.Text = _listZ.Count;
}
在我看来,除了以下之外没有其他方法可以缩短它:
private void setLabel<genericType>(List<genericType> list)
{
if(list is List<A>) LabelA.Text = _listA.Count;
else if(list is List<B>) LabelB.Text = _listB.Count;
else if(list is List<C>) LabelC.Text = _listC.Count;
// and so on...
else if(list is List<Z>) LabelZ.Text = _listZ.Count;
}
重载函数名并不会减少代码行数:
private void setLabel(List<A> list)
{
LabelA.Text = _listA.Count;
}
private void setLabel(List<B> list)
{
LabelB.Text = _listB.Count;
}
我更喜欢使用is
运算符来确定设置哪个Label
,因为它保留了space(在这种情况下,50行无意义的括号和25行略- 不同的函数名称)。 但是,一位 Stack Overflow 用户建议我不要 使用泛型,而是使用单独的函数,每个 Label
一个。尽管此解决方案可行,但我宁愿不这样做。
使用 is
运算符 NOT 和明确键入我的函数有什么好处吗?
好处是您的类型检查是静态的,而不是动态的。如果有人将 List<SomeRandomeClassYouDontSupport>
传递给第一个方法,那么代码将编译并且在 运行 时不能正常工作。它要么什么也不做,抛出一个异常,或者你编写它要做的任何事情,但关键是调用者将无法看到他们做错了什么直到他们运行代码.
当您有多个重载时,验证会在编译时完成。如果提供了不受支持的类型,那么 代码甚至不会编译 而不是编译和不工作。
这也是一个重要的语义差异。泛型是这样说的,"This method will work regardless of what the type is"。创建列表时,没有提供正确和错误的类型参数。您可以创建 任何类型的列表。这是对泛型的恰当使用,因为列表是一种概念上的通用数据结构。有多个重载是一种说法,"This finite list of types is supported." 你属于后一种情况,这样调用者就可以更清楚地了解该行为,这样他们就会明白该方法需要做什么 查看它的签名.
说了这么多,看起来这甚至都不是你应该做的情况。如果您真的想让一个方法接受编译时已知的有限数量类型之一作为参数,那么重载是正确的方法,但在您的情况下,您根本不应该这样做。您应该将这些 UI 组件绑定到 中提到的视图。
我不会评论你正在做的事情是否是一个好的做法:)。
如果给定列表缺少标签对您来说不是世界末日,并且如果您依赖标签字段的一些命名约定以便所有标签都被命名,例如 "LabelX" 其中X 是您将用于通用列表的类型,您可以这样做:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Labels
{
class Program
{
static void Main(string[] args)
{
Container c = new Container();
c.ApplyLabels();
}
}
public class A
{
}
public class B
{
}
public class C
{
}
public class Container
{
private Label LabelA = new Label ();
private Label LabelB = new Label ();
private Label LabelC = new Label ();
private List<A> _listA = new List<A> ();
private List<B> _listB = new List<B> ();
private List<C> _listC = new List<C> ();
public void ApplyLabels ()
{
var allFields = this.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
Dictionary<Type, FieldInfo> listFields = new Dictionary<Type, FieldInfo>();
Dictionary<Type, FieldInfo> labelMappings = new Dictionary<Type, FieldInfo>();
Dictionary<string, Type> namespacesForListGenericTypes = new Dictionary<string, Type>();
List<FieldInfo> possibleLabelFields = new List<FieldInfo>();
foreach (var field in allFields)
{
if (field.FieldType.IsGenericType)
{
var genericTypeDef = field.FieldType.GetGenericTypeDefinition();
if (genericTypeDef == typeof (List<>))
{
var genericArgument = field.FieldType.GetGenericArguments()[0];
listFields.Add(genericArgument, field); // remember list fields and for each list what generic type it has!
namespacesForListGenericTypes[genericArgument.Name] = genericArgument;
}
}
else if (typeof (Label).IsAssignableFrom (field.FieldType))
{
possibleLabelFields.Add(field);
}
}
foreach (var possible in possibleLabelFields)
{
if (possible.Name.Length < 6) continue;
var typeName = possible.Name.Substring(5);
Type genericListType;
if (namespacesForListGenericTypes.TryGetValue (typeName, out genericListType))
{
labelMappings[genericListType] = possible;
}
}
foreach (var list in listFields)
{
FieldInfo destination;
if (false == labelMappings.TryGetValue (list.Key, out destination))
{
continue;
}
var destinationLabel = destination.GetValue(this) as Label;
if (destinationLabel == null) continue;
var listValue = list.Value.GetValue(this) as IList;
var cnt = listValue == null ? 0 : listValue.Count;
destinationLabel.Text = cnt.ToString();
}
}
}
public class Label
{
public string Text { get; set; }
}
}
为什么不自己制作 类 自动派生自己的字段?
private class ListWithText : List<T>
{
int Text {
get { return this.Count; }
}
}
ListWithText<A> LabelA = new ListWithText<A>();
Console.WriteLine(LabelA.Text);
假设 A
到 Z
是我定义的 26 类。在以下示例中:
private List<A> _listA;
private List<B> _listB;
// private List<C>, and so on, through...
private List<Z> _listZ;
private void setLabelA()
{
LabelA.Text = _listA.Count;
}
// private void setLabelB() exists
// and so does setLabelC()
// and so on, all the way through to...
private void setLabelZ()
{
LabelA.Text = _listZ.Count;
}
在我看来,除了以下之外没有其他方法可以缩短它:
private void setLabel<genericType>(List<genericType> list)
{
if(list is List<A>) LabelA.Text = _listA.Count;
else if(list is List<B>) LabelB.Text = _listB.Count;
else if(list is List<C>) LabelC.Text = _listC.Count;
// and so on...
else if(list is List<Z>) LabelZ.Text = _listZ.Count;
}
重载函数名并不会减少代码行数:
private void setLabel(List<A> list)
{
LabelA.Text = _listA.Count;
}
private void setLabel(List<B> list)
{
LabelB.Text = _listB.Count;
}
我更喜欢使用is
运算符来确定设置哪个Label
,因为它保留了space(在这种情况下,50行无意义的括号和25行略- 不同的函数名称)。 但是,一位 Stack Overflow 用户建议我不要 使用泛型,而是使用单独的函数,每个 Label
一个。尽管此解决方案可行,但我宁愿不这样做。
使用 is
运算符 NOT 和明确键入我的函数有什么好处吗?
好处是您的类型检查是静态的,而不是动态的。如果有人将 List<SomeRandomeClassYouDontSupport>
传递给第一个方法,那么代码将编译并且在 运行 时不能正常工作。它要么什么也不做,抛出一个异常,或者你编写它要做的任何事情,但关键是调用者将无法看到他们做错了什么直到他们运行代码.
当您有多个重载时,验证会在编译时完成。如果提供了不受支持的类型,那么 代码甚至不会编译 而不是编译和不工作。
这也是一个重要的语义差异。泛型是这样说的,"This method will work regardless of what the type is"。创建列表时,没有提供正确和错误的类型参数。您可以创建 任何类型的列表。这是对泛型的恰当使用,因为列表是一种概念上的通用数据结构。有多个重载是一种说法,"This finite list of types is supported." 你属于后一种情况,这样调用者就可以更清楚地了解该行为,这样他们就会明白该方法需要做什么 查看它的签名.
说了这么多,看起来这甚至都不是你应该做的情况。如果您真的想让一个方法接受编译时已知的有限数量类型之一作为参数,那么重载是正确的方法,但在您的情况下,您根本不应该这样做。您应该将这些 UI 组件绑定到
我不会评论你正在做的事情是否是一个好的做法:)。
如果给定列表缺少标签对您来说不是世界末日,并且如果您依赖标签字段的一些命名约定以便所有标签都被命名,例如 "LabelX" 其中X 是您将用于通用列表的类型,您可以这样做:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Labels
{
class Program
{
static void Main(string[] args)
{
Container c = new Container();
c.ApplyLabels();
}
}
public class A
{
}
public class B
{
}
public class C
{
}
public class Container
{
private Label LabelA = new Label ();
private Label LabelB = new Label ();
private Label LabelC = new Label ();
private List<A> _listA = new List<A> ();
private List<B> _listB = new List<B> ();
private List<C> _listC = new List<C> ();
public void ApplyLabels ()
{
var allFields = this.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
Dictionary<Type, FieldInfo> listFields = new Dictionary<Type, FieldInfo>();
Dictionary<Type, FieldInfo> labelMappings = new Dictionary<Type, FieldInfo>();
Dictionary<string, Type> namespacesForListGenericTypes = new Dictionary<string, Type>();
List<FieldInfo> possibleLabelFields = new List<FieldInfo>();
foreach (var field in allFields)
{
if (field.FieldType.IsGenericType)
{
var genericTypeDef = field.FieldType.GetGenericTypeDefinition();
if (genericTypeDef == typeof (List<>))
{
var genericArgument = field.FieldType.GetGenericArguments()[0];
listFields.Add(genericArgument, field); // remember list fields and for each list what generic type it has!
namespacesForListGenericTypes[genericArgument.Name] = genericArgument;
}
}
else if (typeof (Label).IsAssignableFrom (field.FieldType))
{
possibleLabelFields.Add(field);
}
}
foreach (var possible in possibleLabelFields)
{
if (possible.Name.Length < 6) continue;
var typeName = possible.Name.Substring(5);
Type genericListType;
if (namespacesForListGenericTypes.TryGetValue (typeName, out genericListType))
{
labelMappings[genericListType] = possible;
}
}
foreach (var list in listFields)
{
FieldInfo destination;
if (false == labelMappings.TryGetValue (list.Key, out destination))
{
continue;
}
var destinationLabel = destination.GetValue(this) as Label;
if (destinationLabel == null) continue;
var listValue = list.Value.GetValue(this) as IList;
var cnt = listValue == null ? 0 : listValue.Count;
destinationLabel.Text = cnt.ToString();
}
}
}
public class Label
{
public string Text { get; set; }
}
}
为什么不自己制作 类 自动派生自己的字段?
private class ListWithText : List<T>
{
int Text {
get { return this.Count; }
}
}
ListWithText<A> LabelA = new ListWithText<A>();
Console.WriteLine(LabelA.Text);