How to implement a generic collection search method that can return a class structure or nullable?

I need a typed helper search function for a heterogeneous collection: it should return a struct or class, otherwise null if the element is not found. Below is an example of using a trivial search for a collection, but it could be a database call or something else.

Is there a way to achieve this with a single method signature?

public T GetClass<T>(string key) where T : class { object o; if (Contents.TryGetValue(key, out o)) { return o as T; } return null; } public T? GetStruct<T>(string key) where T : struct { object o; if (Contents.TryGetValue(key, out o)) { if (o is T) { return (T?) o; } } return null; } 

What I already tried:

  • I understand that general restrictions cannot be used to disambiguate overloads. Therefore, I cannot just give the two methods the same name.
  • Returning (Default) T not an option, since 0 is a valid int value.
  • I tried calling with <int ?> As a type, but as discussed, Nullable<T> not a reference type.

Is there any way to indicate that I will return to the int box?

+6
source share
3 answers

The following method works for both classes and NULL structures:

 public static T GetValue<T>(string key) { object o; if (Contents.TryGetValue(key, out o)) { if (o is T) { return (T)o; } } return default(T); } 

Using:

 int? result1 = GetValue<int?>("someInt"); string result2 = GetValue<string>("someString"); 

Please note that ? is part of the generic type argument and is not defined by the return type method.

+3
source

Is there a way to achieve this with a single method signature?

There's a terrible (truly disgusting) way to do this using optional parameters so that the calling code can look the same in both cases. This is not good, though.

Parameters:

  • Return a Tuple<T, bool> instead of using nullity
  • Use the out parameter (e.g. int.TryParse etc.)
  • Use different method names

Note that by specifying the absence of a value separately, you can make null valid "found" result, which can sometimes be useful. Or you can simply guarantee that it will never be returned.

If you really want to use nullity, I would choose the latter option. I believe this will make your code clearer anyway. IMO, overload should really only be used when the methods do exactly the same thing, expressed using different parameters, while returning Nullable<T> as the return type in one case and T , since the return type in the other case cannot be really saw this way.

+6
source

This should do exactly what you need. If the requested type is a type with a null value, check the base type before casting.

 public static T GetValue<T>(string key) { object o; if (Contents.TryGetValue(key, out o)) { if (o is T || Nullable.GetUnderlyingType(typeof(T)) == o.GetType()) { return (T)o; } } return default(T); } 

My test code is:

 Contents.Add("a string", "string value"); Contents.Add("an integer", 1); Contents.Add("a nullable integer", new Nullable<int>(2)); // Get objects as the type we originally used. Debug.WriteLine(string.Format("GetValue<string>(\"a string\") = {0}", GetValue<string>("a string"))); Debug.WriteLine(string.Format("GetValue<int>(\"an integer\") = {0}", GetValue<int>("an integer"))); Debug.WriteLine(string.Format("GetValue<int?>(\"a nullable integer\") = {0}", GetValue<int?>("a nullable integer"))); // Get objects as base class object. Debug.WriteLine(string.Format("GetValue<object>(\"a string\") = {0}", GetValue<object>("a string"))); Debug.WriteLine(string.Format("GetValue<object>(\"an integer\") = {0}", GetValue<object>("an integer"))); Debug.WriteLine(string.Format("GetValue<object>(\"a nullable integer\") = {0}", GetValue<object>("a nullable integer"))); // Get the ints as the other type. Debug.WriteLine(string.Format("GetValue<int?>(\"an integer\") = {0}", GetValue<int?>("an integer"))); Debug.WriteLine(string.Format("GetValue<int>(\"a nullable integer\") = {0}", GetValue<int>("a nullable integer"))); // Attempt to get as a struct that it not, should return default value. Debug.WriteLine(string.Format("GetValue<double>(\"a string\") = {0}", GetValue<double>("a string"))); // Attempt to get as a nullable struct that it not, or as a class that it not, should return null. Debug.WriteLine(string.Format("GetValue<double?>(\"a string\") = {0}", GetValue<double?>("a string"))); Debug.WriteLine(string.Format("GetValue<StringBuilder>(\"a string\") = {0}", GetValue<StringBuilder>("a string"))); 

Results:

 GetValue<string>("a string") = string value GetValue<int>("an integer") = 1 GetValue<int?>("a nullable integer") = 2 GetValue<object>("a string") = string value GetValue<object>("an integer") = 1 GetValue<object>("a nullable integer") = 2 GetValue<int?>("an integer") = 1 GetValue<int>("a nullable integer") = 2 GetValue<double>("a string") = 0 GetValue<double?>("a string") = GetValue<StringBuilder>("a string") = 
+1
source

Source: https://habr.com/ru/post/914748/


All Articles