C# 在访问可能存在或不存在的子元素时避免对象空引用异常

C# 在访问可能存在或不存在的子元素时避免对象空引用异常,c#,.net,exception-handling,linq-to-xml,nullreferenceexception,C#,.net,Exception Handling,Linq To Xml,Nullreferenceexception,我有: 包含一些元素的XML。 在此XML中定义或不定义的子元素。 当子元素确实存在时,需要提取其值 如何在不引发对象引用错误的情况下获取值 例如: string sampleXML = "<Root><Tag1>tag1value</Tag1></Root>"; //Pass in <Tag2> and the code works: //string sampleXML = "<Root><Tag1>

我有: 包含一些元素的XML。 在此XML中定义或不定义的子元素。 当子元素确实存在时,需要提取其值

如何在不引发对象引用错误的情况下获取值

例如:

 string sampleXML = "<Root><Tag1>tag1value</Tag1></Root>"; 

//Pass in <Tag2> and the code works: 
//string sampleXML = "<Root><Tag1>tag1value</Tag1><Tag2>tag2Value</Tag2></Root>";    
 XDocument sampleDoc = XDocument.Parse(sampleXML);

//Below code is in another method, the 'sampleDoc' is passed in. I am hoping to change only this code
XElement sampleEl = sampleDoc.Root; 
string tag1 = String.IsNullOrEmpty(sampleEl.Element("Tag1").Value) ? "" : sampleEl.Element("Tag1").Value;

//NullReferenceException:
//Object reference not set to an instance of an object.
string tag2 = String.IsNullOrEmpty(sampleEl.Element("Tag2").Value) ? "" : sampleEl.Element("Tag2").Value;
string sampleXML=“tag1value”;
//传入,代码工作:
//字符串sampleXML=“tag1valuetag2Value”;
XDocument sampleDoc=XDocument.Parse(sampleXML);
//下面的代码在另一个方法中,传入“sampleDoc”。我希望只更改此代码
XElement sampleEl=sampleDoc.Root;
string tag1=string.IsNullOrEmpty(sampleEl.Element(“tag1”).Value)?“”:sampleEl.Element(“Tag1”).Value;
//NullReferenceException:
//对象引用未设置为对象的实例。
string tag2=string.IsNullOrEmpty(sampleEl.Element(“tag2”).Value)?“”:sampleEl.Element(“Tag2”).Value;

首先,您应该检查文档是否为空,记住您正在访问.Value,这将引发空引用异常,因此在应用该值之前。请执行测试:

if (sampleEl != null)
  //now apply .value
或三元:

string tag2 = sampleEl.Element("Tag2") != null ? sampleEL.Element("Tag2").Value : String.Empty
然后,您的代码变成:

 string sampleXML = "<Root><Tag1>tag1value</Tag1></Root>"; 

    //Pass in <Tag2> and the code works: 
    //string sampleXML = "<Root><Tag1>tag1value</Tag1><Tag2>tag2Value</Tag2></Root>";    
     XDocument sampleDoc = XDocument.Parse(sampleXML);

    //Below code is in another method, the 'sampleDoc' is passed in. I am hoping to change only this code
    XElement sampleEl = sampleDoc.Root; 
    string tag1 = sampleEl.Element("Tag1") != null ? sampleEl.Element("Tag1").Value : String.Empty;

    //NullReferenceException:
    //Object reference not set to an instance of an object.
string tag2 = sampleEl.Element("Tag2") != null ? sampleEL.Element("Tag2").Value : String.Empty
string sampleXML=“tag1value”;
//传入,代码工作:
//字符串sampleXML=“tag1valuetag2Value”;
XDocument sampleDoc=XDocument.Parse(sampleXML);
//下面的代码在另一个方法中,传入“sampleDoc”。我希望只更改此代码
XElement sampleEl=sampleDoc.Root;
字符串tag1=样本元素(“tag1”)!=无效的元素(“Tag1”)。值:String.Empty;
//NullReferenceException:
//对象引用未设置为对象的实例。
字符串tag2=样本元素(“tag2”)!=无效的sampleEL.Element(“Tag2”)。值:String.Empty
您可以使用快捷方式:

 string tag1= (string)sampleEl.Element("Tag1") ?? string.Empty;

这还使用了一个事实,即LINQ to XML允许强制转换操作获取元素的值(在本例中为强制转换为字符串),但如果元素不存在,则返回
null

您需要检查
null
,以防止它们。考虑到您使用的重复模式,我会将其分解为一个扩展方法

public static string GetElementValue(this XElement parent, string elementName) {
  if (parent == null) { 
    return string.Empty;
  }
  var element = parent.Element(elementName);
  if (element == null || element.Value == null) {
    return string.Empty;
  }
  return element.Value;
}
现在,您的上述代码可以替换为以下代码

string tag1 = sampleEl.GetElementValue("Tag1");
string tag2 = sampleEl.GetElementValue("Tag2");
C#三元运算符在这方面非常好:

string tag2 = sampleEl.Element("Tag2") == null ? "" : sampleEl.Element("Tag2").Value;

我想出了这个扩展方法。它要求您指定要作为lambda访问的属性,以及在实际值或链上的任何内容为null时使用的默认值:

public static TOut ValueOrDefault<TIn, TOut>(this TIn input, Func<TIn, TOut> projection, TOut defaultValue)
        where TOut : class
    {
        try
        {
            return projection(input) ?? defaultValue;
        }
        catch (NullReferenceException)
        {
            return defaultValue;
        }
        catch (InvalidOperationException)
        {
            return defaultValue;
        }
    }

如果topObject、ChildObject、AnotherChild或ChildProperty为空,则值将为空字符串。如果所有这些都是有效的引用,那么返回的将是实际的ChildProperty(可能仍然是空字符串)。NullReferenceException的捕获处理null引用的子成员的引用。对于可为null的类型,在访问可为null的类型的Value属性时会引发InvalidOperationException。

为了好玩,这里有一个使用LINQ to XML的解决方案

  • 只需要一个语句和
  • 不需要三元运算符,因此不需要两次指定标记的名称:

    string tag1 = sampleEl.Elements("Tag1").Select(x => x.Value).FirstOrDefault();
    string tag2 = sampleEl.Elements("Tag2").Select(x => x.Value).FirstOrDefault();
    
添加
??“
如果不存在标记,则希望使用空字符串而不是
null

C#6.0允许我们使表达式更短、更简单:

 string uniqueIdentifier = myNode.Document?.Element("elem1")?.Element("elem2")?.Attribute("attribute1")?.Value;

您可以使用
default(T)
来避免使用约束。我有一个只返回默认值(TOut)的重载,但我认为这可能更有用,因为看起来他想要空字符串。非常好。。。我不知道你能给一个string@JonH:为什么?[…我不喜欢最小15个字符的要求…]@Heinzi-这是个人偏好。我不喜欢看到代码中有一堆“”。看到一个常量更容易阅读。@Heinzi:这对代码的运行方式没有任何重大影响。如果sampleEl.Elements(“Tag1”)为空,则只需使用您认为最可读的内容(就像您更喜欢放置大括号的位置),这仍然会爆炸。Linq运算符的源不能为null。@KeithS:注意我写的是
元素
,而不是
元素
Elements
返回一个
IEnumerable(XElement)
,该值从不为空。它可能是空的,在这种情况下,Select也将返回一个空IEnumerable,FirstOrDefault将返回null(根据需要)。谢谢大家!我没想到会有这么多人帮我(而且在这么短的时间内)。我希望我能把你所有的答案都标对,但我已经把你的答案投了赞成票。我在各种情况下都遇到过空引用异常,将另存一天:)作为一句话:这是使用Visual Basic更容易使用的罕见情况之一:
Dim tag2=sampleXML.Root..Value
属性返回一个(可能是空的)Xelement集合,
Value
扩展方法返回第一个项的字符串值,或者
Nothing
否则返回。在上面的方法定义中,“return element.Value.ToString()”将是必需的,因为我将所有内容都用作字符串。或者“Value”已经是字符串类型了?@艾米:是的,它是字符串:
string tag1 = sampleEl.Elements("Tag1").Select(x => x.Value).FirstOrDefault();
string tag2 = sampleEl.Elements("Tag2").Select(x => x.Value).FirstOrDefault();
 string uniqueIdentifier = myNode.Document?.Element("elem1")?.Element("elem2")?.Attribute("attribute1")?.Value;