假设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
,因为它保留了空间(在这个场景中,50行无意义的括号和25行略有不同的函数名)。然而, Stack Overflow用户建议我不使用泛型,而是使用单独的函数,每个Label
一个。虽然这个解决办法可以,但我宁愿不这样做。
is
操作符和显式键入函数有什么好处吗? 这样做的好处是类型检查是静态的,而不是动态的。如果有人将List<SomeRandomeClassYouDontSupport>
传递给第一个方法,那么代码将编译并且在运行时不能正常工作。它要么什么都不做,抛出一个异常,或者你写代码让它做什么,但关键是调用者在运行代码之前无法看到他们做错了。
当您有多个重载时,验证将在编译时完成。如果提供了不支持的类型,则代码甚至不会编译,而不是编译但不能工作。
这也是一个重要的语义差异。泛型的作用是说,"无论是什么类型,这个方法都可以工作"。在创建列表时,不需要提供正确或错误的类型参数。您可以创建任何类型的列表。这是对泛型的适当使用,因为列表在概念上是一种泛型数据结构。有几个重载是一种说明"支持这个有限类型列表"的方式。您处于后一种情况,因此这使得调用者更清楚该行为,因此他们将通过查看其签名来理解方法需要做什么。
说了这么多,看起来这甚至不是你应该做的事情。如果您真的想让一个方法在编译时接受有限数量的已知类型中的一种作为参数,那么重载是正确的方法,但在您的情况下,您根本不应该这样做。您应该将这些UI组件绑定到这个注释中提到的视图。
为什么不创建自己的类来自动派生它们自己的字段呢?
private class ListWithText : List<T>
{
int Text {
get { return this.Count; }
}
}
ListWithText<A> LabelA = new ListWithText<A>();
Console.WriteLine(LabelA.Text);
我不会评论你所做的是否是一个好的实践:)。
如果没有给定列表的标签对你来说并不是世界末日,如果你依赖于标签字段的一些命名约定,以便所有标签都命名为"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; }
}
}