【问题标题】:How to automatically throw a Custom Exception on NullReferenceException for a specific class?如何在 NullReferenceException 上为特定类自动抛出自定义异常?
【发布时间】:2017-01-07 14:38:39
【问题描述】:
public class Dog 
{

    public string Name { get; set;}
}

然后在这种情况下将 null 传递给 FeedDog()

public void FeedDog(Dog dog)
{

    Console.WriteLine("Feeding "+dog.Name); // throws NullReferenceException
}

我该怎么做才能让它抛出DogDoesNotExistException而不这样做

public void FeedDog(Dog dog)
{

    if (dog == null)
        throw new DogDoesNotExistException(); // want to get rid of this
    Console.WriteLine("Feeding "+dog.Name);
}

所以当你这样做时

public void FeedDog(Dog dog)
{

    Console.WriteLine("Feeding "+dog.Name); // throws DogDoesNotExistException
}

【问题讨论】:

  • GetDog 应该抛出异常。
  • 这是一个糟糕的设计。并非每个NullReferenceException 都应在此类中转换为DogDoesNotExistException。这很容易出错。你需要明确。你真的需要抛出异常吗?如果原因是异常冒泡并在不同的层上处理,那么可以,但为什么不使用 TryXXX 模式返回布尔值?
  • 对,我会更新我的问题
  • 你不能这样做。你没有这种程度的控制。异常是在 CLR 的内部深处生成的。为了比较,他们只是试图帮助发现哪个变量在未来的 C# 版本中负责 NRE
  • @Damien_The_Unbeliever 如果您在下面回答,我可以接受您的回答

标签: c# .net


【解决方案1】:
public void FeedDog(Dog dog)
{

    try
    {
        Console.WriteLine("Feeding " + dog.Name); 
    }
    catch(NullReferenceException e)
    {
        throw new DogDoesnotExistException();
    }
}

【讨论】:

  • 谢谢,我知道这是这样做的方法,但只是希望有一种方法可以“自动”做到这一点,就像这个类有一个空引用异常抛出这种类型的异常
  • @bman 我建议你不要做你所说的。这是纯粹的邪恶!
【解决方案2】:

一种方法是捕获global error handler 中的错误并从那里重新抛出,但这会影响每个类。

因此,您应该考虑使用 AOP(请参阅 what-is-aspect-oriented-programming)。

例如,如果您使用依赖注入,则可以使用拦截器

http://docs.autofac.org/en/latest/advanced/interceptors.html

这个附加类将包装每个方法调用。带有重新抛出的 try catch 逻辑可以在那里定位。如果您确实想这样做,我会建议将原始异常添加为内部异常。

如果你在使用 MVC,你也可以使用过滤器属性。

说了这么多,我会小心捕捉这样的 Null Ref 错误,因为它们非常通用。

【讨论】:

  • 对,我没有说我在 MVC 上这样做是为了检查模型并现在查看过滤器属性。
【解决方案3】:

在将null 传递给FeedDog 时,不应抛出DogDoesNotExistException。要么检查null 并抛出ArgumentNullException 来表示FeedDog 使用不正确,或者如果该方法用于狗实例无论如何都应该存在的上下文中,则根本不检查它。

如果你实际上没有狗,你就不会去你的柜子里拿出狗粮然后把它放在碗里。如果你已经知道你有一只狗,你只会得到并准备食物。

所以如果你没有狗,你就不会打电话给FeedDogdognull)。相反,您会先确保狗存在:

var dog = GetDogFromSomewhere();

if (dog == null)
{
    throw new DogDoesNotExistException();
}

FeedDog(dog);

【讨论】:

    【解决方案4】:

    我不明白你为什么需要这样做,但一种方法是使用包装对象的结构并将结构作为方法参数传递。

    有一个例子:

    struct DogWrapper : IEquatable<DogWrapper>
    {
        readonly Dog _value;
    
        public Dog Value
        {
            get
            {
                if (_value == null)
                {
                    throw new DogDoesNotExistException();
                }
    
                return _value;
            }
        }
    
        public DogWrapper(Dog value)
        {
            _value = value;
        }
    
        // TODO 
        //public override int GetHashCode()
        //{
        //    return base.GetHashCode();
        //}
    
        //public override bool Equals(object obj)
        //{
        //    return base.Equals(obj);
        //}
    
        //public override string ToString()
        //{
        //    return base.ToString();
        //}
    
        public bool Equals(DogWrapper other)
        {
            throw new NotImplementedException();
        }
    
        public static implicit operator DogWrapper(Dog value)
        {
            return new DogWrapper(value);
        }
    }
    

    用法:

    public void FeedDog(DogWrapper dog)
    {
        Console.WriteLine("Feeding "+dog.Name); // now throws DogDoesNotExistException
    }
    

    隐式转换操作符让你以同样的方式调用方法:

    Dog dog = GetDog();
    
    FeedDog(dog);
    

    【讨论】:

    • 这是一个有趣的答案,但我无法理解它,(不是双关语)。我会阅读更多关于此的内容
    【解决方案5】:

    另一种方法是避免在任何地方使用 null,而是创建一个名为“EmptyDog”的派生类,该类在任何属性访问时都会引发异常。

    public class EmptyDog : Dog
    {
       public override Name {get { throw new DogDoesNotExistException(); } }
       ...
    }
    

    在 Dog 类中为此创建一个单例:

     public static readonly Dog EmptyDog = new EmptyDog();
    

    现在您可以在任何地方使用此 EmptyDog 而不是空 Dog,或者您可以用一行来防止空值,然后在有人实际尝试调用该 Dog's Name 时依靠属性覆盖来引发异常。

    例如

      public void SomeMethod (Dog dog)
      {
         dog = dog ?? Dog.EmptyDog;
    
         // access properties and get an exception if dog was null
      }
    

    null object pattern

    【讨论】:

      猜你喜欢
      • 2011-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-08
      • 1970-01-01
      • 2012-10-28
      • 2020-08-03
      相关资源
      最近更新 更多