情况:
我有一些代码可以是:
- a) 表现不佳(反射)
- b) 重复
- c) 难以维护
我希望有一种方法可以获得可维护的非重复代码,而不会像Reflection那样具有高性能。
a)一种性能刨削功能。
static public SelectListItem ToSelectListItem(this Object item, string textProperty, string valueProperty, bool isSelected)
{
return new SelectListItem()
{
Selected = false,
Text = item.GetType().GetProperty(textProperty).GetValue(item, null),
Value = item.GetType().GetProperty(valueProperty).GetValue(item, null)
};
}
b)12个函数,其中代码90%相似。
static public SelectListItem ToSelectListItem(this Class1 type)
{
return new SelectListItem()
{
Selected = false,
Text = type.Name,
Value = type.LoadInfoTypeID.ToString() // This ID is the only thing that changes.
};
}
c)1需要一些稍微难以维护编码的功能。
static public SelectListItem ToSelectListItem(this Object type)
{
int ID = type is Class1 ? (type as Class1).SomeID :
type is Class2 ? (type as Class2).AnotherID :
type is ClassN ? (type as ClassN).YetAnotherID :
-1;
return new SelectListItem()
{
Selected = false,
Text = type.Name, // Ignore the fact that Object doesn't technically have a Name Prop for the moment.
Value = ID.ToString()
};
}
问题:
有没有一种比上面的代码更易于维护、不重复的方式不会抑制性能(至少不会过度)?
如果我必须使用情况c),这是可以接受的,但我想知道是否有我不知道的替代方案。
也:
这些类(Class1、Class2等)是实体框架中的实体。我不确定这是否会添加/删除任何可用选项。
让有问题的类遵守一个特定的接口,该接口允许根据需要对数据进行通用操作。
该接口提供了一个可供未来程序员理解的维护合同,并允许在反射操作之外以类型一致的方式处理实例。
似乎有一种模式是公认的;因此,使用这种设计只是在现有流程上工作。辅助接口(?)可以处理其他实例,即其他处理的5%。
您有一些选择(没有像AutoMapper这样为您做这类事情的第三方库)。
一种方法是在你的类上使用一个接口,并将其传入(请参阅OmegaMan的答案)
另一种选择是使用一个函数,该函数接受一些委托来决定要使用的类型。
static public SelectListItem ToSelectListItem<TItem>(this TItem type, Func<TItem, string> nameSelector, Func<Titem, string> valueSelector)
{
return new SelectListItem()
{
Selected = false,
Text = nameSelector(type)
Value = valueSelector(type)
};
}
//elsewhere
Class1 item1 = ...
item1.ToSelectListItem(a=>a.Name, a=>a.SomeId);
Class2 item2 = ...
item2.ToSelectListItem(a=>a.Name, a=>a.AnotherID);
ClassN itemN = ...
itemN.ToSelectListItem(a=>a.Name, a=>a.YetAnotherID);
也许一个类似于a)但没有反射的解决方案就足够了?此外,您不必使用字符串名称来引用属性。
public static SelectListItem<T>(T o, Func<T, string> textFunc, Func<T, string> valueFunc)
{
return new SelectListItem
{
Selected = false,
Text = textFunc(o),
Value = valueFunc(o)
}
}
您可以在EF生成的代码中添加属性。大多数模板都会为此生成分部。所以你可以有:
public interface ITextValue
{
string GetValue();
string GetText();
}
// for each entity
public partial class SomeEntity : ITextValue // this for each EF-type
{
public GetValue() { return this.ID.ToString(); }
public GetValue() { return this.Name; }
}
然后
static public SelectListItem ToSelectListItem(this ITextValue obj)
{
return new SelectListItem()
{
Selected = false,
Text = obj.GetText(),
Value = obj.GetValue()
};
}
然而,如果你不想修改你的EF类型,你可以使用这种方法:
public interface ITextValue
{
string GetValue(object obj);
string GetText(object obj);
}
// for each entity
public class SomeEntityReader : ITextValue // this for each EF-type
{
public GetValue(object obj) { return ((SomeEntity).ID).ToString(); }
public GetValue(object obj) { return ((SomeEntity)this).Name; }
}
// somewhere central
var Readers = new Dictionary<Type,ITextValue>()
Readers.Add(typeof(SomeEntity), new SomeEntityReader());
// etc
然后
static public SelectListItem ToSelectListItem(this object item)
{
var reader = Readers(typeof(item));
return new SelectListItem()
{
Selected = false,
Text = reader.GetText(),
Value = reader.GetValue()
};
}
我建议让您的类从某个基类派生,该基类将暴露一些将被重写的ID
字段或方法GetId
。
public abstract class YourAbstractClass
{
public abstract int ID { get; set; }
}
public class ConcreteClass1 : YourAbstractClass
{
public override int ID
{
get { return SomeID }
set { SomeID = value }
}
}
如果是物业,您可能需要用NotMappedAttribute
装饰ID
。