Как создать .NET интерфейс со статическими членами? - PullRequest
12 голосов
/ 15 января 2009

В .NET 3.5 я хотел бы создать одноэлементный интерфейс:

interface ISingleton <T>
{
  public static T Instance {get;}
}

Конечно, это не работает, но это то, что я хотел бы. Есть предложения?

РЕДАКТИРОВАТЬ: я просто хочу, чтобы было известно, что все сингелтон будет иметь статическое свойство с именем Instance типа класса. Это всегда там. Интерфейс был бы явным способом выразить это.

Ответы [ 8 ]

12 голосов
/ 15 января 2009

Интерфейс, насколько мне известно, не может быть синглтоном, поскольку его на самом деле не существует. Интерфейс - это Контракт, которому должна следовать реализация. Таким образом, реализация может быть одноэлементной, но интерфейс не может.

7 голосов
/ 15 января 2009

Несмотря на то, что я согласен с другими авторами, что синглтоны очень часто используются, возможное решение вашего вопроса - предоставить абстрактный базовый класс с параметром типа производного синглтона:

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() { } 
}

Прежде чем использовать что-то подобное, хотя вам следует подумать о том, требуется ли синглтон или вам лучше использовать обычный статический класс.

4 голосов
/ 15 января 2009

Хорошо, я сделал этот ответ в вики, потому что я просто собираюсь высказать мнение, которое имеет отношение к вашему вопросу.

Лично я считаю, что синглтоны используются слишком часто, это тот случай, когда IMO на самом деле достаточно редкий, в большинстве случаев статический класс будет гораздо лучше подходить для этого варианта использования, а в других случаях лучше всего будет создать созданный на фабрике неизменный объект. выбор, фактический синглтон гораздо реже, чем думают люди.

У меня не было бы интерфейса, чтобы описать общий шаблон для этого, так как я бы на самом деле хотел, чтобы каждое использование синглтона было продуманным и обоснованным.

2 голосов
/ 15 января 2009

Я знаю, что это не твой вопрос, но сколько у тебя таких синглетонов, что тебе нужен интерфейс? Это пахнет как плохой дизайн для меня - можете ли вы объяснить, почему эти классы должны быть единичными, а не экземплярами? Если ваш ответ - память, я бы посоветовал вам переосмыслить ваше приложение, и, если вы действительно обеспокоены этим, посмотрите на шаблон flyweight (или, возможно, простой заводской шаблон). Извините, что не ответил на вопрос напрямую, но это не похоже на отличную идею.

2 голосов
/ 15 января 2009

Я просто хочу, чтобы было известно, что все синглтоны будут иметь статическое свойство с именем Instance типа класса. Это всегда там. Интерфейс был бы явным способом выразить это.

Напишите вместо этого модульный тест.

1 голос
/ 15 января 2009

Кроме того, как вы утверждаете, он не работает, как бы вы использовали этот интерфейс и реализующий класс?

Вы можете попробовать использовать интерфейс в заводском стиле

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; } }
}
0 голосов
/ 15 января 2009

Как уже указывалось, вы не можете сделать это, и есть веские причины, почему вы не должны.

Метод, который я реализовал в прошлом, создает интерфейс и абстрактный базовый класс, который реализует интерфейс. Это выглядит примерно так:

public interface IMyCompanySetting
{
    XmlNode Serialize();
    IMyCompanySetting Deserialize(XmlNode pattern);
    string SettingName { get; }

string Key {get; } объект SettingValue {get; задавать; } 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;
}

}
0 голосов
/ 15 января 2009

Учитывая классическое понятие ООП интерфейса, которое определяет его как Контракт между реализующими классами, вы не можете добавить в него такую ​​вещь, как статический метод. Если бы вы могли это сделать, вы бы закончили чем-то более похожим на абстрактный класс, в котором у вас есть частичная реализация вашего класса и другие части, требуемые для расширяющихся классов.

...