В .NET 3.5 я хотел бы создать одноэлементный интерфейс:
interface ISingleton <T>
{
public static T Instance {get;}
}
Конечно, это не работает, но я бы хотел. Какие-либо предложения?
Обновлено: Я просто хочу, чтобы было известно, что все одиночные элементы будут иметь статическое свойство с именем Экземпляр типа класса. Он всегда есть. Интерфейс был бы явным способом выразить это.





Насколько мне известно, интерфейс не может быть синглтоном, поскольку на самом деле его не существует. Интерфейс - это контракт, которому должна следовать реализация. Таким образом, реализация может быть одноэлементной, а интерфейс - нет.
Вы не можете указать одноэлементный класс из интерфейса, поскольку интерфейс определяет только контракт, который реализующий класс может выполнить любым способом, который он считает нужным.
AFAIK в C# интерфейс не может иметь статический член. Однако есть экземпляры, в которых статический член был бы полезен. Например, вам нужен КОНТРАКТ реализаций, но все эти реализации нуждаются в классификаторе, который является общим для экземпляров объекта. Это не первый раз, когда мне понадобилась такая вещь.
Кроме того, как вы заявляете, это не работает, как бы вы использовали этот интерфейс и реализующий класс?
Вы можете попробовать интерфейс в фабричном стиле
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; } }
}
Это не ответ, почему вы не использовали функцию добавления комментариев?
Учитывая классическое понятие ООП интерфейса, которое определяет его как контракт между реализующими классами, вы не можете добавить к нему такую вещь, как статический метод. Если бы вы могли это сделать, вы бы закончили чем-то более похожим на абстрактный класс, в котором у вас есть частичная реализация вашего класса и других частей, требуемых для расширяющихся классов.
I just want it to be known that all singeltons will have a static property named Instance of the class type. It is always there. An interface would be an explicit way to express this.
Вместо этого напишите модульный тест.
Как уже указывалось, вы не можете этого сделать, и есть веские причины, по которым вам не следует этого делать.
Метод, который я реализовал в прошлом, создает интерфейс и абстрактный базовый класс, реализующий интерфейс. Это выглядит примерно так:
public interface IMyCompanySetting
{
XmlNode Serialize();
IMyCompanySetting Deserialize(XmlNode pattern);
string SettingName { get; }
строка Key {получить; } объект SettingValue {получить; набор; } SettingScope Scope {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;
}
}
Хорошо, я сделал этот ответ вики, потому что я просто собираюсь высказать мнение, которое имеет отношение к вашему вопросу.
Я лично думаю, что синглтоны используются слишком часто, это вариант использования, который IMO на самом деле достаточно редок, в большинстве случаев статический класс подойдет для варианта использования намного лучше, а в других случаях просто созданный фабрикой неизменяемый объект является лучшим выбором, фактический синглтон встречается гораздо реже, чем думают люди.
У меня не было бы интерфейса, описывающего общий шаблон для этого, поскольку я бы действительно хотел, чтобы каждое использование синглтона было тщательно продумано и обосновано.
Я знаю, что это не ваш вопрос, а сколько у вас синглтонов, требующих интерфейса? Для меня это пахнет плохим дизайном - можете ли вы четко объяснить, почему эти классы должны быть одиночными, а не экземплярами? Если ваш ответ - память, я бы посоветовал вам переосмыслить свое приложение, и если вы действительно обеспокоены, изучите шаблон легкого веса (или, возможно, простой заводской шаблон). Извините за то, что не ответил на вопрос напрямую, но это не кажется хорошей идеей.
Хотя я согласен с другими плакатами, что синглтоны используются слишком часто, возможное решение вашего вопроса - предоставить абстрактный базовый класс с параметром типа производного синглтона:
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() { }
}
Прежде чем использовать что-то вроде этого, вам действительно стоит подумать о том, требуется ли синглтон или вам лучше использовать обычный статический класс.
Это вообще не работает. MySingleton.Instance всегда имеет значение null, а статический конструктор в конечном итоге вообще не вызывается (VS2008 .NET 3.5)
Ага. Ты прав. Доступ к свойству Instance в Singleton <T> не вызывает вызова статического конструктора в MySingleton. Довольно тривиально переставить это так, чтобы оно работало, но на самом деле, зачем вам это?
Предложение было поддержано 2 года назад
https://github.com/dotnet/csharplang/issues/1711
Но да, пока это невозможно, особенно в dotnet 3.5.
Я думаю, что OP имел в виду нечто иное. Я понимаю следующий вопрос: «Как будет выглядеть описание интерфейса для одноэлементного класса в C#?»