Wie mache ich den Rückgabetyp einer Methode generisch?

165

Gibt es eine Möglichkeit, diese Methode generisch zu gestalten, damit ich einen String, Bool, Int oder Double zurückgeben kann? Im Moment wird eine Zeichenfolge zurückgegeben, aber wenn "true" oder "false" als Konfigurationswert gefunden werden kann, möchte ich beispielsweise einen Bool zurückgeben.

    public static string ConfigSetting(string settingName)
    {  
         return ConfigurationManager.AppSettings[settingName];
    }
MacGyver
quelle
Gibt es eine Möglichkeit, den Typ der einzelnen Einstellungen zu ermitteln?
Thecoshman
2
Ich denke, die Frage, die Sie wirklich stellen möchten, lautet: "Wie kann ich meine Anwendungskonfiguration stark typisieren?" Es ist jedoch zu lange her, dass ich damit gearbeitet habe, um eine richtige Antwort zu schreiben.
Simon
Yah, im Idealfall möchte ich den Typ nicht an die Methode übergeben müssen. Ich werde nur die 4 Typen haben, die ich erwähnt habe. Wenn also "true" / "false" gesetzt ist, möchte ich, dass diese Funktion einen Booleschen Wert zurückgibt (ohne ihn an die Methode übergeben zu müssen). Ich kann wahrscheinlich int und double zu nur double kombinieren, und alles andere sollte eine Zeichenfolge sein. Was bereits beantwortet wurde, wird gut funktionieren, aber ich muss den Typ jedes Mal übergeben, was wahrscheinlich in Ordnung ist.
MacGyver
3
Ihr Kommentar klingt so, als würden Sie nach einer Methode fragen, die zur Laufzeit einen stark typisierten Bool (oder String oder Int oder was Sie haben) zurückgibt, basierend auf den tatsächlichen Daten, die für den Einstellungsnamenschlüssel abgerufen wurden. C # wird das nicht für Sie tun; Es gibt keine Möglichkeit, den Typ dieses Werts zur Kompilierungszeit zu ermitteln. Mit anderen Worten, das ist dynamisches Tippen, nicht statisches Tippen. C # kann das für Sie tun, wenn Sie das dynamicSchlüsselwort verwenden. Dafür fallen Leistungskosten an, aber beim Lesen einer Konfigurationsdatei sind die Leistungskosten mit ziemlicher Sicherheit unbedeutend.
Phoog

Antworten:

352

Sie müssen es zu einer generischen Methode machen, wie folgt:

public static T ConfigSetting<T>(string settingName)
{  
    return /* code to convert the setting to T... */
}

Der Anrufer muss jedoch den Typ angeben, den er erwartet. Sie könnten dann möglicherweise verwenden Convert.ChangeType, vorausgesetzt, alle relevanten Typen werden unterstützt:

public static T ConfigSetting<T>(string settingName)
{  
    object value = ConfigurationManager.AppSettings[settingName];
    return (T) Convert.ChangeType(value, typeof(T));
}

Ich bin nicht ganz davon überzeugt, dass dies alles eine gute Idee ist, wohlgemerkt ...

Jon Skeet
quelle
21
/ * Code zum Konvertieren der Einstellung in T ... * / und hier folgt der gesamte Roman :)
Adrian Iftode
1
Wäre dies nicht erforderlich, dass Sie den Typ der gewünschten Einstellung kennen, was möglicherweise nicht möglich ist.
Thecoshman
2
@thecoshman: Das würde es, aber wenn du es nicht tust, was machst du dann mit dem zurückgegebenen Wert?
George Duckett
5
Während diese Antwort ist natürlich richtig, und wie Sie erfüllen die OP Wunsch beachten Sie , ist es wahrscheinlich erwähnenswert , dass der alte Ansatz getrennten Verfahrens ( ConfigSettingString, ConfigSettingBoolusw.) den Vorteil der Methode Körpers hat , die kürzer sein wird, klarer und besser fokussierte .
Phoog
4
Wenn dies nicht empfohlen wird, wozu dienen dann generische Rückgabetypen?
Bobbyalex
29

Sie könnten verwenden Convert.ChangeType():

public static T ConfigSetting<T>(string settingName)
{
    return (T)Convert.ChangeType(ConfigurationManager.AppSettings[settingName], typeof(T));
}
Glassplitter
quelle
13

Es gibt viele Möglichkeiten, dies zu tun (nach Priorität aufgelistet, spezifisch für das OP-Problem)

  1. Option 1: Gerader Ansatz - Erstellen Sie mehrere Funktionen für jeden erwarteten Typ, anstatt nur eine generische Funktion zu haben.

    public static bool ConfigSettingInt(string settingName)
    {  
         return Convert.ToBoolean(ConfigurationManager.AppSettings[settingName]);
    }
  2. Option 2: Wenn Sie keine ausgefallenen Konvertierungsmethoden verwenden möchten - Wandeln Sie den Wert in ein Objekt und dann in einen generischen Typ um.

    public static T ConfigSetting<T>(string settingName)
    {  
         return (T)(object)ConfigurationManager.AppSettings[settingName];
    }

    Hinweis - Dies wird einen Fehler auslösen, wenn die Besetzung nicht gültig ist (Ihr Fall). Ich würde dies nicht empfehlen, wenn Sie sich über den Typ Casting nicht sicher sind, sondern Option 3 wählen.

  3. Option 3: Generisch mit Typensicherheit - Erstellen Sie eine generische Funktion für die Typkonvertierung.

    public static T ConvertValue<T,U>(U value) where U : IConvertible
    {
        return (T)Convert.ChangeType(value, typeof(T));
    } 

    Hinweis - T ist der erwartete Typ, beachten Sie die where-Einschränkung hier (Typ von U muss IConvertible sein, um uns vor den Fehlern zu bewahren)

RollerCosta
quelle
4
Warum die dritte Option generisch machen U? Es macht keinen Sinn, dies zu tun, und es macht es schwieriger, die Methode aufzurufen. Akzeptiere einfach IConvertiblestattdessen. Ich denke nicht, dass es sich lohnt, die zweite Option für diese Frage aufzunehmen, da sie die gestellte Frage nicht beantwortet. Sie sollten die Methode wahrscheinlich auch in der ersten Option umbenennen ...
Jon Skeet
7

Sie müssen den Typ Ihres Rückgabewerts der Methode in den generischen Typ konvertieren, den Sie beim Aufruf an die Methode übergeben.

    public static T values<T>()
    {
        Random random = new Random();
        int number = random.Next(1, 4);
        return (T)Convert.ChangeType(number, typeof(T));
    }

Sie müssen einen Typ übergeben, der für den Wert, den Sie über diese Methode zurückgeben, umwandelbar ist.

Wenn Sie einen Wert zurückgeben möchten, dessen Typ nicht in den von Ihnen übergebenen generischen Typ umwandelbar ist, müssen Sie möglicherweise den Code ändern oder sicherstellen, dass Sie einen Typ übergeben, der für den Rückgabewert der Methode umwandelbar ist. Dieser Ansatz wird daher nicht empfohlen.

Vinay Chanumolu
quelle
return (T)Convert.ChangeType(number, typeof(T));Genau
richtig
1

Erstellen Sie eine Funktion und geben Sie den put-Parameter als generischen Typ aus.

 public static T some_function<T>(T out_put_object /*declare as Output object*/)
    {
        return out_put_object;
    }
Prabhakar
quelle
Dies ist für einige Anwendungsfälle eigentlich ziemlich klug. Als würde man Daten aus einer Datenbank ziehen. Sie wissen, dass Sie eine Liste mit Daten vom Typ T erhalten. Die Lademethode weiß einfach nicht, welchen Typ von T Sie JETZT möchten. Übergeben Sie einfach eine neue Liste <WantedObject> an diese, und die Methode kann ihren Job erledigen und die Liste füllen, bevor Sie sie zurückgeben. Nett!
Marco Heumann
0

Bitte versuchen Sie es mit dem folgenden Code:

public T? GetParsedOrDefaultValue<T>(string valueToParse) where T : struct, IComparable
{
 if(string.EmptyOrNull(valueToParse))return null;
  try
  {
     // return parsed value
     return (T) Convert.ChangeType(valueToParse, typeof(T));
  }
  catch(Exception)
  {
   //default as null value
   return null;
  }
 return null;
}
Sid
quelle
-1
 private static T[] prepareArray<T>(T[] arrayToCopy, T value)
    {
        Array.Copy(arrayToCopy, 1, arrayToCopy, 0, arrayToCopy.Length - 1);
        arrayToCopy[arrayToCopy.Length - 1] = value;
        return (T[])arrayToCopy;
    }

Ich habe dies in meinem gesamten Code ausgeführt und wollte einen Weg finden, es in eine Methode zu integrieren. Ich wollte dies hier teilen, weil ich Convert.ChangeType nicht für meinen Rückgabewert verwenden musste. Dies ist möglicherweise keine bewährte Methode, hat aber bei mir funktioniert. Diese Methode verwendet ein Array vom generischen Typ und einen Wert, der am Ende des Arrays hinzugefügt werden soll. Das Array wird dann mit dem ersten entfernten Wert kopiert und der in die Methode aufgenommene Wert wird am Ende des Arrays hinzugefügt. Das Letzte ist, dass ich das generische Array zurückgebe.

Adam Howard
quelle