diff --git a/dotnet/src/webdriver/Command.cs b/dotnet/src/webdriver/Command.cs index 1559fe9690f5a..3a44422a00f84 100644 --- a/dotnet/src/webdriver/Command.cs +++ b/dotnet/src/webdriver/Command.cs @@ -55,11 +55,12 @@ public Command(string name, string jsonParameters) /// Session ID the driver is using /// Name of the command /// Parameters for that command + /// If is . public Command(SessionId? sessionId, string name, Dictionary? parameters) { this.SessionId = sessionId; this.Parameters = parameters ?? new Dictionary(); - this.Name = name; + this.Name = name ?? throw new ArgumentNullException(nameof(name)); } /// diff --git a/dotnet/src/webdriver/CommandInfoRepository.cs b/dotnet/src/webdriver/CommandInfoRepository.cs index fa184c60bda5c..36b19d6ea3b6d 100644 --- a/dotnet/src/webdriver/CommandInfoRepository.cs +++ b/dotnet/src/webdriver/CommandInfoRepository.cs @@ -87,6 +87,7 @@ public bool IsCommandNameDefined(string commandName) /// /// The for which to get the information. /// The for the specified command, or if not found or value is not . + /// If is . public T? GetCommandInfo(string commandName) where T : CommandInfo { T? toReturn = default; diff --git a/dotnet/src/webdriver/WebDriver.cs b/dotnet/src/webdriver/WebDriver.cs index b5e8d5b6437b5..a0be02f770ea0 100644 --- a/dotnet/src/webdriver/WebDriver.cs +++ b/dotnet/src/webdriver/WebDriver.cs @@ -39,9 +39,6 @@ public class WebDriver : IWebDriver, ISearchContext, IJavaScriptExecutor, IFinds /// The default command timeout for HTTP requests in a RemoteWebDriver instance. /// protected static readonly TimeSpan DefaultCommandTimeout = TimeSpan.FromSeconds(60); - - private ICommandExecutor executor; - private ICapabilities capabilities; private IFileDetector fileDetector = new DefaultFileDetector(); private NetworkManager network; private WebElementFactory elementFactory; @@ -52,10 +49,10 @@ public class WebDriver : IWebDriver, ISearchContext, IJavaScriptExecutor, IFinds /// Initializes a new instance of the class. /// /// The object used to execute commands. - /// The object used to configuer the driver session. + /// The object used to configure the driver session. protected WebDriver(ICommandExecutor executor, ICapabilities capabilities) { - this.executor = executor; + this.CommandExecutor = executor; try { @@ -79,7 +76,7 @@ protected WebDriver(ICommandExecutor executor, ICapabilities capabilities) this.network = new NetworkManager(this); this.registeredCommands.AddRange(DriverCommand.KnownCommands); - if ((this as ISupportsLogs) != null) + if (this is ISupportsLogs) { // Only add the legacy log commands if the driver supports // retrieving the logs via the extension end points. @@ -91,18 +88,12 @@ protected WebDriver(ICommandExecutor executor, ICapabilities capabilities) /// /// Gets the which executes commands for this driver. /// - public ICommandExecutor CommandExecutor - { - get { return this.executor; } - } + public ICommandExecutor CommandExecutor { get; } /// /// Gets the that the driver session was created with, which may be different from those requested. /// - public ICapabilities Capabilities - { - get { return this.capabilities; } - } + public ICapabilities Capabilities { get; private set; } /// /// Gets or sets the URL the browser is currently displaying. @@ -115,6 +106,8 @@ public string Url get { Response commandResponse = this.Execute(DriverCommand.GetCurrentUrl, null); + + commandResponse.EnsureValueIsNotNull(); return commandResponse.Value.ToString(); } @@ -129,7 +122,8 @@ public string Title get { Response commandResponse = this.Execute(DriverCommand.GetTitle, null); - object returnedTitle = commandResponse != null ? commandResponse.Value : string.Empty; + object returnedTitle = commandResponse.Value ?? string.Empty; + return returnedTitle.ToString(); } } @@ -142,10 +136,10 @@ public string PageSource { get { - string pageSource = string.Empty; Response commandResponse = this.Execute(DriverCommand.GetPageSource, null); - pageSource = commandResponse.Value.ToString(); - return pageSource; + + commandResponse.EnsureValueIsNotNull(); + return commandResponse.Value.ToString(); } } @@ -158,6 +152,8 @@ public string CurrentWindowHandle get { Response commandResponse = this.Execute(DriverCommand.GetCurrentWindowHandle, null); + + commandResponse.EnsureValueIsNotNull(); return commandResponse.Value.ToString(); } } @@ -170,8 +166,10 @@ public ReadOnlyCollection WindowHandles get { Response commandResponse = this.Execute(DriverCommand.GetWindowHandles, null); + + commandResponse.EnsureValueIsNotNull(); object[] handles = (object[])commandResponse.Value; - List handleList = new List(); + List handleList = new List(handles.Length); foreach (object handle in handles) { handleList.Add(handle.ToString()); @@ -184,51 +182,37 @@ public ReadOnlyCollection WindowHandles /// /// Gets a value indicating whether this object is a valid action executor. /// - public bool IsActionExecutor - { - get { return true; } - } + public bool IsActionExecutor => true; /// /// Gets the for the current session of this driver. /// public SessionId SessionId { get; private set; } +#nullable enable + /// /// Gets or sets the responsible for detecting /// sequences of keystrokes representing file paths and names. /// + /// If value is set to . public virtual IFileDetector FileDetector { - get - { - return this.fileDetector; - } - - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(value), "FileDetector cannot be null"); - } - - this.fileDetector = value; - } + get => this.fileDetector; + set => this.fileDetector = value ?? throw new ArgumentNullException(nameof(value), "FileDetector cannot be null"); } - internal INetwork Network - { - get { return this.network; } - } + internal INetwork Network => this.network; /// /// Gets or sets the factory object used to create instances of /// or its subclasses. /// + /// If value is set to . protected WebElementFactory ElementFactory { - get { return this.elementFactory; } - set { this.elementFactory = value; } + get => this.elementFactory; + set => this.elementFactory = value ?? throw new ArgumentNullException(nameof(value)); } /// @@ -255,7 +239,7 @@ public void Dispose() /// The JavaScript code to execute. /// The arguments to the script. /// The value returned by the script. - public object ExecuteAsyncScript(string script, params object[] args) + public object? ExecuteAsyncScript(string script, params object?[]? args) { return this.ExecuteScriptCommand(script, DriverCommand.ExecuteAsyncScript, args); } @@ -266,7 +250,7 @@ public object ExecuteAsyncScript(string script, params object[] args) /// The JavaScript code to execute. /// The arguments to the script. /// The value returned by the script. - public object ExecuteScript(string script, params object[] args) + public object? ExecuteScript(string script, params object?[]? args) { return this.ExecuteScriptCommand(script, DriverCommand.ExecuteScript, args); } @@ -278,7 +262,7 @@ public object ExecuteScript(string script, params object[] args) /// The arguments to the script. /// The value returned by the script. /// If is . - public object ExecuteScript(PinnedScript script, params object[] args) + public object? ExecuteScript(PinnedScript script, params object?[]? args) { if (script == null) { @@ -288,6 +272,8 @@ public object ExecuteScript(PinnedScript script, params object[] args) return this.ExecuteScript(script.MakeExecutionScript(), args); } +#nullable restore + /// /// Finds the first element in the page that matches the object /// @@ -321,7 +307,9 @@ public virtual IWebElement FindElement(string mechanism, string value) Dictionary parameters = new Dictionary(); parameters.Add("using", mechanism); parameters.Add("value", value); + Response commandResponse = this.Execute(DriverCommand.FindElement, parameters); + return this.GetElementFromResponse(commandResponse); } @@ -357,10 +345,14 @@ public virtual ReadOnlyCollection FindElements(string mechanism, st Dictionary parameters = new Dictionary(); parameters.Add("using", mechanism); parameters.Add("value", value); + Response commandResponse = this.Execute(DriverCommand.FindElements, parameters); + return this.GetElementsFromResponse(commandResponse); } +#nullable enable + /// /// Gets a object representing the image of the page on the screen. /// @@ -368,7 +360,9 @@ public virtual ReadOnlyCollection FindElements(string mechanism, st public Screenshot GetScreenshot() { Response screenshotResponse = this.Execute(DriverCommand.Screenshot, null); - string base64 = screenshotResponse.Value.ToString(); + + screenshotResponse.EnsureValueIsNotNull(); + string base64 = screenshotResponse.Value.ToString()!; return new Screenshot(base64); } @@ -386,7 +380,9 @@ public PrintDocument Print(PrintOptions printOptions) } Response commandResponse = this.Execute(DriverCommand.Print, printOptions.ToDictionary()); - string base64 = commandResponse.Value.ToString(); + + commandResponse.EnsureValueIsNotNull(); + string base64 = commandResponse.Value.ToString()!; return new PrintDocument(base64); } @@ -409,6 +405,7 @@ public void PerformActions(IList actionSequenceList) Dictionary parameters = new Dictionary(); parameters["actions"] = objectList; + this.Execute(DriverCommand.Actions, parameters); } @@ -463,8 +460,6 @@ public INavigation Navigate() return new Navigator(this); } -#nullable enable - /// /// Executes a command with this driver. /// @@ -527,28 +522,25 @@ internal bool RegisterDriverCommand(string commandName, [NotNullWhen(true)] Comm return false; } -#nullable restore - /// /// Find the element in the response /// /// Response from the browser - /// Element from the page - internal IWebElement GetElementFromResponse(Response response) + /// Element from the page, or if the response does not contain a dictionary. + /// If is . + internal IWebElement? GetElementFromResponse(Response response) { if (response == null) { throw new NoSuchElementException(); } - WebElement element = null; - Dictionary elementDictionary = response.Value as Dictionary; - if (elementDictionary != null) + if (response.Value is Dictionary elementDictionary) { - element = this.elementFactory.CreateElement(elementDictionary); + return this.elementFactory.CreateElement(elementDictionary); } - return element; + return null; } /// @@ -559,13 +551,11 @@ internal IWebElement GetElementFromResponse(Response response) internal ReadOnlyCollection GetElementsFromResponse(Response response) { List toReturn = new List(); - object[] elements = response.Value as object[]; - if (elements != null) + if (response.Value is object?[] elements) { - foreach (object elementObject in elements) + foreach (object? elementObject in elements) { - Dictionary elementDictionary = elementObject as Dictionary; - if (elementDictionary != null) + if (elementObject is Dictionary elementDictionary) { WebElement element = this.elementFactory.CreateElement(elementDictionary); toReturn.Add(element); @@ -576,12 +566,15 @@ internal ReadOnlyCollection GetElementsFromResponse(Response respon return toReturn.AsReadOnly(); } +#nullable restore + /// /// Executes commands with the driver /// /// Command that needs executing /// Parameters needed for the command /// WebDriver Response + /// If is . internal Response InternalExecute(string driverCommandToExecute, Dictionary parameters) { return Task.Run(() => this.InternalExecuteAsync(driverCommandToExecute, parameters)).GetAwaiter().GetResult(); @@ -593,6 +586,7 @@ internal Response InternalExecute(string driverCommandToExecute, DictionaryCommand that needs executing /// Parameters needed for the command /// A task object representing the asynchronous operation + /// If is . internal Task InternalExecuteAsync(string driverCommandToExecute, Dictionary parameters) { @@ -605,6 +599,7 @@ internal Task InternalExecuteAsync(string driverCommandToExecute, /// A value representing the command to execute. /// A containing the names and values of the parameters of the command. /// A containing information about the success or failure of the command and any data returned by the command. + /// If is . protected virtual Response Execute(string driverCommandToExecute, Dictionary parameters) { @@ -617,11 +612,12 @@ protected virtual Response Execute(string driverCommandToExecute, /// A value representing the command to execute. /// A containing the names and values of the parameters of the command. /// A containing information about the success or failure of the command and any data returned by the command. + /// If is . protected virtual async Task ExecuteAsync(string driverCommandToExecute, Dictionary parameters) { Command commandToExecute = new Command(SessionId, driverCommandToExecute, parameters); - Response commandResponse = await this.executor.ExecuteAsync(commandToExecute).ConfigureAwait(false); + Response commandResponse = await this.CommandExecutor.ExecuteAsync(commandToExecute).ConfigureAwait(false); if (commandResponse.Status != WebDriverResult.Success) { @@ -636,6 +632,7 @@ protected virtual async Task ExecuteAsync(string driverCommandToExecut /// /// Capabilities of the browser [MemberNotNull(nameof(SessionId))] + [MemberNotNull(nameof(Capabilities))] protected void StartSession(ICapabilities capabilities) { Dictionary parameters = new Dictionary(); @@ -645,8 +642,7 @@ protected void StartSession(ICapabilities capabilities) // and end nodes are compliant with the W3C WebDriver Specification, // and therefore will already contain all of the appropriate values // for establishing a session. - RemoteSessionSettings remoteSettings = capabilities as RemoteSessionSettings; - if (remoteSettings == null) + if (capabilities is not RemoteSessionSettings remoteSettings) { Dictionary matchCapabilities = this.GetCapabilitiesDictionary(capabilities); @@ -665,15 +661,14 @@ protected void StartSession(ICapabilities capabilities) Response response = this.Execute(DriverCommand.NewSession, parameters); - Dictionary rawCapabilities = response.Value as Dictionary; - if (rawCapabilities == null) + response.EnsureValueIsNotNull(); + if (response.Value is not Dictionary rawCapabilities) { string errorMessage = string.Format(CultureInfo.InvariantCulture, "The new session command returned a value ('{0}') that is not a valid JSON object.", response.Value); throw new WebDriverException(errorMessage); } - ReturnedCapabilities returnedCapabilities = new ReturnedCapabilities(rawCapabilities); - this.capabilities = returnedCapabilities; + this.Capabilities = new ReturnedCapabilities(rawCapabilities); string sessionId = response.SessionId ?? throw new WebDriverException($"The remote end did not respond with ID of a session when it was required. {response.Value}"); this.SessionId = new SessionId(sessionId); @@ -686,11 +681,17 @@ protected void StartSession(ICapabilities capabilities) /// A Dictionary consisting of the capabilities requested. /// This method is only transitional. Do not rely on it. It will be removed /// once browser driver capability formats stabilize. + /// If is . protected virtual Dictionary GetCapabilitiesDictionary(ICapabilities capabilitiesToConvert) { + if (capabilitiesToConvert is null) + { + throw new ArgumentNullException(nameof(capabilitiesToConvert)); + } + Dictionary capabilitiesDictionary = new Dictionary(); - IHasCapabilitiesDictionary capabilitiesObject = capabilitiesToConvert as IHasCapabilitiesDictionary; - foreach (KeyValuePair entry in capabilitiesObject.CapabilitiesDictionary) + + foreach (KeyValuePair entry in ((IHasCapabilitiesDictionary)capabilitiesToConvert).CapabilitiesDictionary) { if (CapabilityType.IsSpecCompliantCapabilityName(entry.Key)) { @@ -740,18 +741,20 @@ protected virtual void Dispose(bool disposing) } finally { - this.SessionId = null; + this.SessionId = null!; } - this.executor.Dispose(); + + this.CommandExecutor.Dispose(); } +#nullable enable + private static void UnpackAndThrowOnError(Response errorResponse, string commandToExecute) { // Check the status code of the error, and only handle if not success. if (errorResponse.Status != WebDriverResult.Success) { - Dictionary errorAsDictionary = errorResponse.Value as Dictionary; - if (errorAsDictionary != null) + if (errorResponse.Value is Dictionary errorAsDictionary) { ErrorResponse errorResponseObject = new ErrorResponse(errorAsDictionary); string errorMessage = errorResponseObject.Message; @@ -804,25 +807,25 @@ private static void UnpackAndThrowOnError(Response errorResponse, string command // TODO(JimEvans): Handle the case where the unexpected alert setting // has been set to "ignore", so there is still a valid alert to be // handled. - string alertText = string.Empty; - if (errorAsDictionary.ContainsKey("alert")) + string? alertText = null; + if (errorAsDictionary.TryGetValue("alert", out object? alert)) { - Dictionary alertDescription = errorAsDictionary["alert"] as Dictionary; - if (alertDescription != null && alertDescription.ContainsKey("text")) + if (alert is Dictionary alertDescription + && alertDescription.TryGetValue("text", out object? text)) { - alertText = alertDescription["text"].ToString(); + alertText = text?.ToString(); } } - else if (errorAsDictionary.ContainsKey("data")) + else if (errorAsDictionary.TryGetValue("data", out object? data)) { - Dictionary alertData = errorAsDictionary["data"] as Dictionary; - if (alertData != null && alertData.ContainsKey("text")) + if (data is Dictionary alertData + && alertData.TryGetValue("text", out object? dataText)) { - alertText = alertData["text"].ToString(); + alertText = dataText?.ToString(); } } - throw new UnhandledAlertException(errorMessage, alertText); + throw new UnhandledAlertException(errorMessage, alertText ?? string.Empty); case WebDriverResult.NoAlertPresent: throw new NoAlertPresentException(errorMessage); @@ -867,10 +870,8 @@ private static void UnpackAndThrowOnError(Response errorResponse, string command throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "{0} ({1})", errorMessage, errorResponse.Status)); } } - else - { - throw new WebDriverException("The " + commandToExecute + " command returned an unexpected error. " + errorResponse.Value.ToString()); - } + + throw new WebDriverException($"The {commandToExecute} command returned an unexpected error. {errorResponse.Value}"); } } @@ -881,9 +882,9 @@ private static void UnpackAndThrowOnError(Response errorResponse, string command /// The name of the command to execute. /// The arguments to the script. /// The value returned by the script. - protected object ExecuteScriptCommand(string script, string commandName, params object[] args) + protected object? ExecuteScriptCommand(string script, string commandName, params object?[]? args) { - object[] convertedArgs = ConvertArgumentsToJavaScriptObjects(args); + object?[] convertedArgs = ConvertArgumentsToJavaScriptObjects(args); Dictionary parameters = new Dictionary(); parameters.Add("script", script); @@ -901,19 +902,16 @@ protected object ExecuteScriptCommand(string script, string commandName, params return this.ParseJavaScriptReturnValue(commandResponse.Value); } - private static object ConvertObjectToJavaScriptObject(object arg) + private static object? ConvertObjectToJavaScriptObject(object? arg) { - IWrapsElement argAsWrapsElement = arg as IWrapsElement; - IWebDriverObjectReference argAsObjectReference = arg as IWebDriverObjectReference; - IEnumerable argAsEnumerable = arg as IEnumerable; - IDictionary argAsDictionary = arg as IDictionary; + IWebDriverObjectReference? argAsObjectReference = arg as IWebDriverObjectReference; - if (argAsObjectReference == null && argAsWrapsElement != null) + if (argAsObjectReference == null && arg is IWrapsElement argAsWrapsElement) { argAsObjectReference = argAsWrapsElement.WrappedElement as IWebDriverObjectReference; } - object converted = null; + object? converted; if (arg is string || arg is float || arg is double || arg is int || arg is long || arg is bool || arg == null) { @@ -924,24 +922,24 @@ private static object ConvertObjectToJavaScriptObject(object arg) Dictionary webDriverObjectReferenceDictionary = argAsObjectReference.ToDictionary(); converted = webDriverObjectReferenceDictionary; } - else if (argAsDictionary != null) + else if (arg is IDictionary argAsDictionary) { // Note that we must check for the argument being a dictionary before // checking for IEnumerable, since dictionaries also implement IEnumerable. // Additionally, JavaScript objects have property names as strings, so all // keys will be converted to strings. - Dictionary dictionary = new Dictionary(); - foreach (var key in argAsDictionary.Keys) + Dictionary dictionary = new Dictionary(); + foreach (DictionaryEntry argEntry in argAsDictionary) { - dictionary.Add(key.ToString(), ConvertObjectToJavaScriptObject(argAsDictionary[key])); + dictionary.Add(argEntry.Key.ToString()!, ConvertObjectToJavaScriptObject(argEntry.Value)); } converted = dictionary; } - else if (argAsEnumerable != null) + else if (arg is IEnumerable argAsEnumerable) { - List objectList = new List(); - foreach (object item in argAsEnumerable) + List objectList = new List(); + foreach (object? item in argAsEnumerable) { objectList.Add(ConvertObjectToJavaScriptObject(item)); } @@ -961,11 +959,11 @@ private static object ConvertObjectToJavaScriptObject(object arg) /// /// The arguments. /// The list of the arguments converted to JavaScript objects. - private static object[] ConvertArgumentsToJavaScriptObjects(object[] args) + private static object?[] ConvertArgumentsToJavaScriptObjects(object?[]? args) { if (args == null) { - return new object[] { null }; + return new object?[] { null }; } for (int i = 0; i < args.Length; i++) @@ -976,20 +974,17 @@ private static object[] ConvertArgumentsToJavaScriptObjects(object[] args) return args; } - private object ParseJavaScriptReturnValue(object responseValue) + private object? ParseJavaScriptReturnValue(object? responseValue) { - object returnValue = null; - - Dictionary resultAsDictionary = responseValue as Dictionary; - object[] resultAsArray = responseValue as object[]; + object? returnValue; - if (resultAsDictionary != null) + if (responseValue is Dictionary resultAsDictionary) { if (this.elementFactory.ContainsElementReference(resultAsDictionary)) { returnValue = this.elementFactory.CreateElement(resultAsDictionary); } - else if (ShadowRoot.TryCreate(this, resultAsDictionary, out ShadowRoot shadowRoot)) + else if (ShadowRoot.TryCreate(this, resultAsDictionary, out ShadowRoot? shadowRoot)) { returnValue = shadowRoot; } @@ -1006,15 +1001,14 @@ private object ParseJavaScriptReturnValue(object responseValue) returnValue = resultAsDictionary; } } - else if (resultAsArray != null) + else if (responseValue is object?[] resultAsArray) { bool allElementsAreWebElements = true; - List toReturn = new List(); - foreach (object item in resultAsArray) + List toReturn = new List(resultAsArray.Length); + foreach (object? item in resultAsArray) { - object parsedItem = this.ParseJavaScriptReturnValue(item); - IWebElement parsedItemAsElement = parsedItem as IWebElement; - if (parsedItemAsElement == null) + object? parsedItem = this.ParseJavaScriptReturnValue(item); + if (parsedItem is not IWebElement) { allElementsAreWebElements = false; } @@ -1024,11 +1018,10 @@ private object ParseJavaScriptReturnValue(object responseValue) if (toReturn.Count > 0 && allElementsAreWebElements) { - List elementList = new List(); - foreach (object listItem in toReturn) + List elementList = new List(resultAsArray.Length); + foreach (object? listItem in toReturn) { - IWebElement itemAsElement = listItem as IWebElement; - elementList.Add(itemAsElement); + elementList.Add((IWebElement)listItem!); } returnValue = elementList.AsReadOnly(); @@ -1046,8 +1039,6 @@ private object ParseJavaScriptReturnValue(object responseValue) return returnValue; } -#nullable enable - /// /// Creates a Virtual Authenticator. /// @@ -1062,7 +1053,9 @@ public string AddVirtualAuthenticator(VirtualAuthenticatorOptions options) } Response commandResponse = this.Execute(DriverCommand.AddVirtualAuthenticator, options.ToDictionary()); - string id = (string)commandResponse.Value!; + + commandResponse.EnsureValueIsNotNull(); + string id = (string)commandResponse.Value; this.AuthenticatorId = id; return id; } @@ -1126,6 +1119,7 @@ public List GetCredentials() Response getCredentialsResponse = this.Execute(driverCommandToExecute: DriverCommand.GetCredentials, parameters); + getCredentialsResponse.EnsureValueIsNotNull(); if (getCredentialsResponse.Value is not object?[] credentialsList) { throw new WebDriverException($"Get credentials call succeeded, but the response was not a list of credentials: {getCredentialsResponse.Value}"); diff --git a/dotnet/src/webdriver/WebElementFactory.cs b/dotnet/src/webdriver/WebElementFactory.cs index eb6428354e121..80ff907ebdc2f 100644 --- a/dotnet/src/webdriver/WebElementFactory.cs +++ b/dotnet/src/webdriver/WebElementFactory.cs @@ -49,7 +49,7 @@ public WebElementFactory(WebDriver parentDriver) /// /// The dictionary containing the element reference. /// A containing the information from the specified dictionary. - public virtual WebElement CreateElement(Dictionary elementDictionary) + public virtual WebElement CreateElement(Dictionary elementDictionary) { string elementId = this.GetElementId(elementDictionary); return new WebElement(this.ParentDriver, elementId); @@ -60,7 +60,7 @@ public virtual WebElement CreateElement(Dictionary elementDictio /// /// The dictionary to check. /// if the dictionary contains an element reference; otherwise, . - public bool ContainsElementReference(Dictionary elementDictionary) + public bool ContainsElementReference(Dictionary elementDictionary) { if (elementDictionary == null) { @@ -78,7 +78,7 @@ public bool ContainsElementReference(Dictionary elementDictionar /// If is . /// If the dictionary does not contain the element reference property name. /// If the element property is or . - public string GetElementId(Dictionary elementDictionary) + public string GetElementId(Dictionary elementDictionary) { if (elementDictionary == null) { @@ -90,13 +90,13 @@ public string GetElementId(Dictionary elementDictionary) throw new ArgumentException("elementDictionary", "The specified dictionary does not contain an element reference"); } - string? elementId = elementIdObj.ToString(); + string? elementId = elementIdObj?.ToString(); if (string.IsNullOrEmpty(elementId)) { throw new InvalidOperationException("The specified element ID is either null or the empty string."); } - return elementId; + return elementId!; } } }