如何防止调用者通过调用静态“CreateInstance()”方法创建C#/.Net 7类对象 *EXCEPT*?

nfs0ujit  于 2023-06-25  发布在  .NET
关注(0)|答案(4)|浏览(130)

我有一个抽象基类“UserFeedback”,以及两个子类“A”和“B”。
UserFeedback有一个静态的“CreateInstance()”方法。
我想 * 防止 * 调用者直接示例化一个新的UserFeedback对象:

public abstract class UserFeedback
{
    public string ComponentType { get; set; }
    public string UserName { get; set; }
    public string EMail { get; set; }

    protected UserFeedback(string componentType)
    {
        ComponentType = componentType;
    }

    public static UserFeedback CreateInstance(string componentType)
    {
        switch (componentType)
        {
            case "A":
                return new A(componentType);  // Line 32
            case "B":
                return new B(componentType);  // Line 34
            default:
                throw new ArgumentException(String.Format("User Feedback Type: {0}", componentType));
        }
    }

    public abstract string GetTitle(string operation);
}

public class A : UserFeedback
{
    protected A(string componentType) : base(componentType)
    { }

    public override string GetTitle(string operation)
    {
        return "A Feedback";
    }
}

public class B : UserFeedback
{
    protected B(string componentType) : base(componentType)
    { }

    public override string GetTitle(string operation)
    {
        return "B Feedback";
    }
}

这在第32和34行给出了编译错误:

Error   CS0122  'A.A(string)' is inaccessible due to its protection level

在C#/.NET的新版本中,有没有“简单”的方法来实现这一点?
或者我应该放弃,让A()和B()的构造函数“公共”?

iyr7buue

iyr7buue1#

您可以简单地将A和B设置为私有Nested Types

public abstract class UserFeedback
{
    public string ComponentType { get; set; }
    public string UserName { get; set; }
    public string EMail { get; set; }

    protected UserFeedback(string componentType)
    {
        ComponentType = componentType;
    }

    public static UserFeedback CreateInstance(string componentType)
    {
        switch (componentType)
        {
            case "A":
                return new A(componentType);  // Line 32
            case "B":
                return new B(componentType);  // Line 34
            default:
                throw new ArgumentException(String.Format("User Feedback Type: {0}", componentType));
        }
    }

    public abstract string GetTitle(string operation);

    private class A : UserFeedback
    {
        public A(string componentType) : base(componentType)
        { }

        public override string GetTitle(string operation)
        {
            return "A Feedback";
        }
    }

    private class B : UserFeedback
    {
        public B(string componentType) : base(componentType)
        { }

        public override string GetTitle(string operation)
        {
            return "B Feedback";
        }
    }
}
ekqde3dh

ekqde3dh2#

如果您真的不希望客户端创建派生类的示例,而只希望它们通过静态CreateInstance方法,一种可能的方法是将派生类设置为private,并将它们与Userfeedback放在同一个类中。如果您确实采用这种方法,则无法强制转换到这些示例,因为它们不可见

public abstract class UserFeedback
{
    public string ComponentType { get; set; }
    public string UserName { get; set; }
    public string EMail { get; set; }

    protected UserFeedback(string componentType)
    {
        ComponentType = componentType;
    }

    public static UserFeedback CreateInstance(string componentType)
    {
        switch (componentType)
        {
            case "A":
                return new A(componentType);  // Line 32
            case "B":
                return new B(componentType);  // Line 34
            default:
                throw new ArgumentException(String.Format("User Feedback Type: {0}", componentType));
        }
    }
    public abstract string GetTitle(string operation);
    private class A : UserFeedback
    {
        public A(string componentType) : base(componentType)
        { }

        public override string GetTitle(string operation)
        {
            return "A Feedback";
        }
    }

    private class B : UserFeedback
    {
        public B(string componentType) : base(componentType)
        { }

        public override string GetTitle(string operation)
        {
            return "B Feedback";
        }
    }
}
gwo2fgha

gwo2fgha3#

在您的代码中,子类A可以接收任何值,即使只有A是有效的。为什么在子类中甚至需要构造函数参数?如果不需要它,为什么不把它公开,让调用者示例化它呢?
如果上面的情况成立,让调用者直接示例化AB实现。这些对象具有示例化自身所需的信息,不应该需要构造函数参数来告诉它们。
首先,让我们从抽象类中删除静态CreateInstance方法。

public abstract class UserFeedback
{
    public string ComponentType { get; set; }
    public string UserName { get; set; }
    public string EMail { get; set; }

    protected UserFeedback(string componentType)
    {
        ComponentType = componentType;
    }

    public abstract string GetTitle(string operation);
}

接下来,在子类中,将构造函数设为公共无参数构造函数,因为我们不需要外部任何东西来告诉我们AB是什么。它们已经知道它们是什么,所以我们可以将其硬编码到基构造函数调用中。
这还有一个额外的好处,即在实现新的子类时不必更新基本UserFeedback类。
我在我的例子中使用了nameof()来去掉字符串。

public class A : UserFeedback
{
    public A() : base(nameof(A))
    { }

    public override string GetTitle(string operation)
    {
        return "A Feedback";
    }
}

public class B : UserFeedback
{
    public B() : base(nameof(B))
    { }

    public override string GetTitle(string operation)
    {
        return "B Feedback";
    }
}
1l5u6lss

1l5u6lss4#

有一个不同的选择。有时候,当出现这种情况时,嵌套类是不可取的,或者难以实现。
当它发生时,protected internal构造函数是你的朋友。只有您自己的程序集可以调用基构造函数,因此只有您的程序集可以示例化它。
我习惯于看到CreateInstance()做一些很好的事情,比如访问基于#if的几个实现中的一个或其他奇特的东西。还有一种我不完全理解的机制,通过这种机制,一个特定的程序集可以在请求时加载,它可以访问protected external构造函数,并且这个程序集在编译时是由你的程序集祝福的,所以没有其他人可以做它。

相关问题