目前我正在尝试实现一个系统或一种创建类型的方法,但在创建时也会获取类型名称。
所以说我有这样的行为,可以由应用程序的用户创建:
Blur
Contrast
Sharpen
Diffuse
Invert
...
那么在通过UI创建名称时,我应该如何提取他们的名字。所以说你们每个人都有单独的UI按钮。每次创建其中一个时,都必须将它们命名为Blur01
,Blur02
,......这些对象将以数百万的形式创建。
所以目前他们有.Name
属性必须在创建时设置。但我也不想在每个类中复制代码来设置名称。
我应该使用哪种设计模式?
答案 0 :(得分:2)
如果您希望每种类型都拥有自己独特的增量器,那么您将拥有重复的代码。您可以通过使用Type和int在基类中保存静态字典来避免这种情况,例如:
private static Dictionary<Type,int> nameSuffixCounters =
new Dictionary<Type,int>();
public string CreateName()
{
Type type = this.GetType();
return string.Format("{0}{1}",type.Name, this.GetNextAndIncrement(type));
}
private int GetNextAndIncrement(Type type)
{
int current;
if (!this.nameSuffixCounters.TryGetValue(type, out current))
{
current = 0;
}
nameSuffixCounters[type] = ++current;
return current;
}
这样的东西可以摆脱重复的代码,但它增加了在基类中使用反射的一些复杂性......
答案 1 :(得分:2)
使用这种模式可以为undo/redo capability奠定基础,看起来你可能需要它。
简而言之,您可以创建一个名为“Command”的接口,从中可以派生出名为“Blur”,“Sharpen”等的子类。 Command将定义一个execute()方法,该方法将根据需要由子类实现:
public interface Command
{
public void execute();
public String getName();
}
public class Blur implements Command
{
// ...
public Blur(String name)
{
// ...
}
//...
public void execute()
{
// execute the blur command
}
}
许多UI工具包都支持这种模式 - 例如Swing。所以你可能不需要做太多工作就可以了。
答案 2 :(得分:1)
我不确定为什么(大多数)答案会提到反思 - 好的ol'多态性对此很好。获取运行时类型名称很简单 - 它只是this.GetType().Name
。
唯一有点棘手的部分是每班保持独特的计数器。之前已在SO上讨论过 - 但我找不到ATM链接。但是,最重要的是,您可以(ab)使用泛型或维持Dictionary<Type, int>
来跟踪。
泛型版本有效,因为每个不同的类型参数都会创建一个不同的泛型类 - 所以它们每个都有不同的静态计数器:
abstract class Command<T> where T : Command<T> {
private static int counter = 1;
private static object syncLock = new object();
protected Command() {
lock (syncLock) {
_name = this.GetType().Name + counter++.ToString();
}
}
public string Name { get { return _name; } }
private string _name;
}
class Blur : Command<Blur> { }
class Sharpen : Command<Sharpen> { }
不需要覆盖派生类中的任何内容,但您必须确保继承的Command<T>
是正确的 - 或者您将共享计数器。我还使用了一个锁来同步对计数器的访问 - 假设你将从多个线程创建实例。
如果您不喜欢通用版本,那么Dictionary<Type, int>
也可以正常工作。
abstract class Command {
private static Dictionary<Type, int> counter = new Dictionary<Type, int>();
private static object syncLock = new object();
protected Command() {
Type t = this.GetType();
lock (syncLock) {
if (!counter.ContainsKey(t)) {
counter.Add(t, 0);
}
_name = t.Name + counter[t]++.ToString();
}
}
public string Name { get { return _name; } }
private string _name;
}
class Blur : Command { }
class Sharpen : Command { }
由于你将创建每个“数百万”,我可能会分析两者并检查内存使用情况。我并不特别关心通用方法 - 因为它很容易搞砸并传递错误的类型参数 - 但我怀疑它的内存使用情况要好一些。另外,如果您要创建&gt; int.MaxValue
,你必须担心溢出。
所有这一切,我不确定为什么你不会只维护索引可寻址集合并通过索引引用它们 - 如Blur[0]
?
答案 3 :(得分:0)
我担心在一个地方无法做到这一点。你必须在每个类定义中明确地“键入”类(类型)名称。原因是基类中的任何反射都将返回基类类型,而不是具体类型...除非你覆盖每个派生类中的方法,在这种情况下你也可以将类型名称硬编码为字符串const (以避免反射命中)...
那么,
呢public const string TypeName = "MYTypeName";
答案 4 :(得分:0)
如果它不必是人类可读的,我会使用GUID作为对象的名称,并有一个基本字段来指示它是从中创建的基本操作。每个对象可以唯一标识为(基本名称) - (ID)。你可以有一个人类可读的描述字段但不用于查找。如果你确实需要显示一个GUID,我使用的技巧是从Base 16转换到Base 34.基本上从0123456789ABCDEF到0123456789ABCDEFGHJKLMNPQRSTUVWXZY。这会产生一个更短的字符串,更容易显示和输入。
答案 5 :(得分:-1)
您最好的选择是在virtual
的基类中创建一个属性(或函数,如果您愿意)并提供一种基本名称。像...这样的东西。
public virtual string BaseName
{
get { return GetType().Name; }
}
您的.Name
媒体资源可以保持不变,或者更改为.NameSuffix
,您可以将BaseName
和NameSuffix
合并为一个完整的名称。
最后,这可能不是解决这个问题的最好方法。命名这些对象/动作的目的是什么?它看起来像你在图像处理中使用命令模式,你要么设置一系列动作来执行,要么想要保持一系列动作来撤消。这样的系统更适合链接列表结构,您可以在某种图形中显示(如果需要),显示操作(可能是类的名称,或上面定义的BaseName
)和他们将被执行的系列。
至于你需要使用名称来实例化对象,我不太清楚为什么。如果这确实是必要的,那么您只有两个选项可以是工厂模式,或者对类的名称或自定义属性值使用反射(如果需要,也可以用于提供名称)。但是,如果您打算将其实例化为“数百万”远离反思。
我想这是一种迂回的说法,一切都取决于这需要如何运作。您必须提供更具体和全面的信息,以获得相应的具体和全面的答案。