如何在C#中防止对象实例化

7
我需要检查传递给构造函数的参数,并在将其视为无效的情况下防止特定对象的实例化。我发现可以抛出异常,因此对象引用将以预期的方式结束为空值。
例如,只有在传递给构造函数的整数为非负数时才会实例化此类。
class MyClass
{
    public MyClass(int a)
    {
        if (a < 0)
        {
            throw new Exception();
        }
    }
}

尽管上述方法可以正常运行,但我相信c#可以提供更简洁的方式来完成此操作,避免每次构造新对象时都需要try/catch所需的额外成本。
static void Main(string[] args)
{
    MyClass e1;
    MyClass e2;

    try
    {
        e1 = new MyClass(1);
    } 
    catch(Exception)   { }

    try
    {
        e2 = new MyClass(-1);
    } 
    catch(Exception) { }
}

1
如果用户向构造函数传递无效值,您想要做什么? - Selman Genç
2
你应该实现工厂模式,并在工厂方法中进行检查,而不是在构造函数中。从构造函数抛出异常是中断实例创建的唯一方式。 - MarcinJuraszek
1
@MarcinJuraszek:如果一个工厂比类本身更了解类的约束条件,那就很奇怪了。 - zerkms
1
@zerkms 你可以从类中公开内部静态方法,并从工厂方法中使用它。或者将工厂方法作为类本身的一部分。 - MarcinJuraszek
1
FYI:在.NET中,类名通常以大写字母开头。(您会注意到代码高亮效果更好。) - Jonathon Reinhart
显示剩余2条评论
3个回答

15

在这种情况下,你应该考虑使用工厂模式。你将构造函数设为private,而是使用静态方法来返回实例。

public class Foo {
    private Foo(int a) { ... }

    public static Foo GetFoo(int a) {
        if (a < 0) {
            throw new Exception("No Foo for you!");

            // or

            return null;
        }

        return new Foo(a);
    }
}

public class Program {
    public static void Main() {
        Foo f;

        f = new Foo();        // Not allowed, ctor is private.

        f = Foo.GetFoo(42);   // Do this instead.
    }
}

通过这个,你可以做一些相当有趣的事情。

在这里,我们有一个 Foo 类,具有不同的子类。通过使用工厂模式,我们可以构造特定 Foo 子类的实例,而外部世界甚至不知道任何子类存在!

public abstract class Foo { 

    // Private implementations of Foo
    // No one outside can ever construct one directly.
    private class RedFoo : Foo { }
    private class GreenFoo : Foo { }
    private class BlueFoo : Foo { }

    public static Foo GetColoredFoo(string color) {

        switch (color.ToLower()) {
        case "red":    return new RedFoo();
        case "green":  return new GreenFoo();
        case "blue":   return new BlueFoo();
        }

        throw new Exception("No Foo for that color!");
    }
}

public class Program {
    public static void Main() {
        Foo f;

        f = new Foo();     // Not allowed; Foo is abstract

        f = new RedFoo();  // Not allowed, RedFoo is private, inside of Foo

        f = Foo.GetColoredFoo("red");  // Returns an instance of RedFoo

    }
}

这将“如何最佳构造你实际需要的对象”的知识移动到类本身的定义中,并且当然消除了try/catch。你可以在静态工厂方法内部应用任何逻辑。


点赞,比我的回答更好的例子。虽然可能会考虑返回 null 而不是抛出异常 - 因为这是他最初的目标。 - Nabren
3
这并没有解决“每次要构造一个新对象时都需要避免try/catch的额外成本”的问题。异常仍然会被抛出。 - TheDude
@TheDude 我添加了第二个示例,以展示工厂模式的真正威力。 - Jonathon Reinhart
@TheDude,我在第一个示例中进行了编辑,展示了他如何返回“null”,这将提供与他的“try”/“catch”代码相同的效果。 - Jonathon Reinhart

4

你可以采用工厂模式,就像MarcinJruaszek建议的那样,将构造函数设为私有,并添加一个静态方法:

public class myClass
{
    private myClass(int a)
    {
        // constructor
    }

   public static myClass Create(int a){
       if (a < 0)
        {
            return null;
        }
        return new myClass(a);
   }
}

然后执行 myClass.Create(1) 方法。


+1. 这是“工厂模式”的一种变体...不确定为什么你的帖子中有“或者这个”。 - Alexei Levenkov
据我理解,工厂模式使用一个单独的类作为工厂。 - TheDude
修改了我的答案以反映它!谢谢! - TheDude
1
准确来说,它是工厂方法模式,不同于像抽象工厂这样的更常见的“工厂”含义。 - Alexei Levenkov

1
我建议您创建一个静态方法,该方法接受您需要验证的参数并返回对象。我不知道在构造函数中如何放弃对象创建而不抛出异常。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接