如何使类公开,并且它的约束不易访问

时间:2014-05-20 11:27:29

标签: c# singleton

我有以下两个班级:

Singleton - >用于定义Singleton

/// <summary>
///     By inheriting from this member you indicate that the implementing class should be implemented as a Singleton.
///     A singleton does mean that only one instance of the Singleton can exists at a time, even when multiple threads
///     are requesting an instance.
/// </summary>
/// <typeparam name="T">The type for which to create a singleton.</typeparam>
public class Singleton<T> where T : class
{
    #region Constructors

    /// <summary>
    ///     A protected constructor to make sure that the compiler doesn't add a default public one.
    ///     A public one would mean that you can create an instance of this class directly which is a violation of the
    ///     Singleton pattern.
    /// </summary>
    protected Singleton()
    {
    }

    #endregion Constructors

    #region Properties

    /// <summary>
    ///     An instance of the <see typeparam="T" />.
    /// </summary>
    private static T instance;

    private static readonly object instanceLock = new object();

    /// <summary>
    ///     Gets an instance of <see typeparam="T" />. When no instance is existing, a new one is created, otherwise the
    ///     already existing
    ///     is returned.
    /// </summary>
    /// <param name="constructionParameters">The parameters that are needed for the construction of this object.</param>
    /// <returns>An instance of <see typeparam="T" />.</returns>
    public static T GetInstance(params object[] constructionParameters)
    {
        if (!typeof(T).IsDefined(typeof(SingletonAttribute), false))
        { throw new InvalidOperationException("Still rework this."); }

        if (instance == null)
        {
            lock (instanceLock)
            {
                if (instance == null)
                {
                    instance = typeof(T).InvokeMember(typeof(T).Name,
                        BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.NonPublic, null, null,
                        constructionParameters, CultureInfo.CurrentCulture) as T;
                }
            }
        }

        return instance;
    }

    /// <summary>
    ///     Clears the current instance.
    /// </summary>
    public static void ClearInstance()
    {
        instance = null;
    }

    #endregion Properties
}

另一个类是一个初始化程序,它允许我将任何对象构造为Singleton。

/// <summary>
///     Provides an easy way to initialize a class as a Singleton.
/// </summary>
/// <typeparam name="T">The type of the class that should be instantiated as a Singleton.</typeparam>
public class SingletonInitializer<T> : Singleton<T> where T : class
{
    #region Constructors

    /// <summary>
    ///     A private constructor that prevents the compiler from adding a default public one.
    /// </summary>
    [ExcludeFromCodeCoverage]
    private SingletonInitializer()
    {

    }

    #endregion Constructors

    #region Methods

    /// <summary>
    ///     Gets an instance of <see typeparam="T" />. When no instance is existing, a new one is created, otherwise the
    ///     already existing
    ///     is returned.
    /// </summary>
    /// <param name="constructionParameters">The parameters that are needed for the construction of this object.</param>
    /// <returns>An instance of <see typeparam="T" />.</returns>
    public static new T GetInstance(params object[] constructionParameters)
    {
        if (!typeof(T).IsDefined(typeof(SingletonAttribute), false))
        { throw new InvalidOperationException("Still rework this."); }

        Singleton<T>.GetInstance(constructionParameters);
        return default(T);
    }

    #endregion
}

到目前为止,这不是问题。我会给你一个小例子:

我将把我作为单身人士加载的课程:

public class Car
{
    #region Constructors

    /// <summary>
    ///     Creates a new instance of a <see cref="Person" />.
    /// </summary>
    protected Car()
    {
    }

    #endregion

    #region Properties

    /// <summary>
    ///     Gets or sets the name of the <see cref="Person" />.
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    ///     Gets or sets the age of the <see cref="Person" />.
    /// </summary>
    public int Age { get; set; }

    #endregion
}

允许通过Singleton范围更改此类属性的代码。

SingletonInitializer<Car>.GetInstance().Name = "Ford";
SingletonInitializer<Car>.GetInstance().Age = 1;

到目前为止,一切正常,但在上面的场景中,用户可以自己创建一个类,让它继承自Singleton,如下所示:

public class Person : Singleton<Person>
{
    #region Constructors

    /// <summary>
    ///     Creates a new instance of a <see cref="Person" />.
    /// </summary>
    protected Person()
    {
    }

    #endregion

    #region Properties

    /// <summary>
    ///     Gets or sets the firstname of the <see cref="Person" />.
    /// </summary>
    public string Firstname { get; set; }

    /// <summary>
    ///     Gets or sets the name of the <see cref="Person" />.
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    ///     Gets or sets the age of the <see cref="Person" />.
    /// </summary>
    public int Age { get; set; }

    #endregion
}

然后,用户可以调用以下代码,通过Singleton范围更改此类的属性:

Person.GetInstance();

我想避免任何人都可以在一个对象上调用GetInstance(),换句话说,除了SingletonInitializer之外,不应该在任何对象上使用GetInstance。如果可能,我想在编译时检测到这一点,并让Visual Studio在遇到这种行为时生成错误。

所以,这是一个非常大的问题,但我想让范围非常明确。 希望有人能让我走上正轨。

1 个答案:

答案 0 :(得分:1)

您可以将课程密封http://msdn.microsoft.com/en-us/library/88c54tsw.aspx或将方法设为内部http://msdn.microsoft.com/en-us/library/7c5ka91b.aspx。密封的方法更安全。

编辑:这是一个实现,它将保证每个类型的应用程序范围一个实例:

    public class SingletonIntializer<T>
{
    public T GetInstance(params object[] @params)
    {
        if (!SingletonContainer.ContainsType(typeof(T)))
        {
            SingletonContainer.CreateInstance(typeof(T), @params);
        }

        return (T)SingletonContainer.GetInstance(typeof(T));
    }
}

internal static class SingletonContainer
{
    private static Dictionary<Type, object> internalContainer = new Dictionary<Type, object>();

    public static bool ContainsType(Type type)
    {
        return internalContainer.ContainsKey(type);
    }

    public static bool CreateInstance(Type type, object[] @params)
    {   
        if (ContainsType(type))
        {
            return false;
        }

        internalContainer[type] = Activator.CreateInstance(type, @params);
        return true;
    }

    public static object GetInstance(Type type)
    {
        if (!ContainsType(type))
        {
            return null;
        }

        return internalContainer[type];
    }
}