在 .NET 3.5 中,我想创建一个单例接口:
interface ISingleton <T>
{
public static T Instance {get;}
}
当然这不起作用,但这是我想要的。有什么建议么?
编辑:我只想知道所有的单例都会有一个名为 Instance 的类类型的静态属性。它总是在那里。接口将是表达这一点的明确方式。
在 .NET 3.5 中,我想创建一个单例接口:
interface ISingleton <T>
{
public static T Instance {get;}
}
当然这不起作用,但这是我想要的。有什么建议么?
编辑:我只想知道所有的单例都会有一个名为 Instance 的类类型的静态属性。它总是在那里。接口将是表达这一点的明确方式。
据我所知,接口不能是单例,因为它实际上并不存在。接口是实现必须遵循的契约。因此,实现可以是单例,但接口不能。
虽然我同意单例被过度使用的其他海报,但您问题的一个可能解决方案是提供一个抽象基类,其类型参数为派生单例:
public abstract class Singleton<T> where T : Singleton<T>
{
private static T _instance;
public static T Instance
{
get { return _instance; }
protected set { _instance = value; }
}
}
任何从 Singleton 派生的类都将具有正确类型的静态 Instance 属性:
public class MySingleton : Singleton<MySingleton>
{
static MySingleton()
{
Instance = new MySingleton();
}
private MySingleton() { }
}
不过,在使用这样的东西之前,您应该真正考虑是否需要单例,或者您是否最好使用普通的静态类。
好的,我将此答案设为 Wiki,因为我将提供与您的问题相切的观点。
我个人认为 Singleton 被过度使用了,它是 IMO 实际上相当罕见的用例,在大多数情况下,静态类更适合用例,而在其他情况下,工厂创建的不可变对象是最好的选择,实际的单身人士比人们想象的要少得多。
我不会有一个接口来描述一个通用模式,因为我实际上希望对单例的每一次使用都进行非常仔细和合理的考虑。
我只想知道所有的单例都会有一个名为 Instance 的类类型的静态属性。它总是在那里。接口将是表达这一点的明确方式。
改为编写单元测试。
我知道这不是你的问题,但你有多少单身人士需要一个接口?这对我来说闻起来像是糟糕的设计——你能清楚地解释为什么这些类应该是单例而不是实例吗?如果您的答案是记忆,我建议您过度考虑您的应用程序,如果您真的担心,请查看享元模式(或者可能是简单的工厂模式)。很抱歉没有直接回答这个问题,但这听起来不是一个好主意。
除此之外,正如您所说,它不起作用,您将如何使用此接口和实现类?
您可以尝试使用工厂风格的界面
interface ISingletonFactory<T>
{
public T Instance {get;}
}
public class SingletonFactory: ISingletonFactory<Singleton>
{
public Singleton Instance {get { return Singleton.Instance;}}
}
public class Singleton
{
private Singleton foo;
public static Singleton Instance { get { return foo; } }
}
鉴于接口的 OOP 的经典概念将其定义为实现类之间的契约,您不能向其中添加静态方法之类的东西。如果你能做到,你会以更类似于抽象类的东西结束,你有你的类的部分实现和扩展类所需的其他部分。
正如所指出的,您不能这样做,并且有充分的理由不应该这样做。
我过去实现的方法创建了一个接口和一个实现该接口的抽象基类。它看起来像这样:
public interface IMyCompanySetting
{
XmlNode Serialize();
IMyCompanySetting Deserialize(XmlNode pattern);
string SettingName { get; }
字符串键 { 获取;} 对象设置值 { 获取;放; } 设置范围范围 { 获取;放; } }
public abstract class MyCompanySettingBase : IMyCompanySetting
{
public MyCompanySettingBase() {}
public MyCompanySettingBase(XmlNode pattern)
{
Deserialize(pattern);
}
#region IMyCompanySetting Members
public abstract XmlNode Serialize();
public abstract IMyCompanySetting Deserialize(XmlNode pattern);
public abstract string SettingName{ get; }
public abstract string Key { get; }
public abstract SettingScope Scope{ get; set; }
public abstract object SettingValue{ get; set; }
#endregion
public static XmlNode WrapInSettingEnvelope(XmlNode innerNode, IMyCompanySetting theSetting)
{
// Write the top of the envelope.
XmlTextWriter xtw = null;
MemoryStream theStream = OpenSettingEnvelope(theSetting, ref xtw);
// Insert the message.
xtw.WriteNode(new XmlTextReader(innerNode.OuterXml, XmlNodeType.Element, null), true);
// Close the envelope.
XmlNode retNode = CloseSettingEnvelope(xtw, theStream);
return retNode;
}
public static MemoryStream OpenSettingEnvelope(IMyCompanySetting theSetting, ref XmlTextWriter theWriter)
{
MemoryStream theStream = new MemoryStream();
theWriter = new XmlTextWriter(theStream, Encoding.ASCII);
System.Type messageType = theSetting.GetType();
string[] fullAssembly = messageType.Assembly.ToString().Split(',');
string assemblyName = fullAssembly[0].Trim();
theWriter.WriteStartElement(theSetting.SettingName);
theWriter.WriteAttributeString("type", messageType.ToString());
theWriter.WriteAttributeString("assembly", assemblyName);
theWriter.WriteAttributeString("scope", ConfigurationManager.ScopeName(theSetting.Scope));
return theStream;
}
public static XmlNode CloseSettingEnvelope(XmlTextWriter xtw, MemoryStream theStream)
{
XmlDocument retDoc = new XmlDocument();
try
{
// Close the envelope.
xtw.WriteEndElement();
xtw.Flush();
// Return the node.
string xmlString = Encoding.ASCII.GetString(theStream.ToArray());
retDoc.LoadXml(xmlString);
}
catch (XmlException)
{
string xmlString = Encoding.ASCII.GetString(theStream.ToArray());
Trace.WriteLine(xmlString);
retDoc.LoadXml(@"<error/>");
}
catch (Exception)
{
retDoc.LoadXml(@"<error/>");
}
return retDoc.DocumentElement;
}
}