Laden...

Type Variable als Type Angabe benutzen

Erstellt von perlfred vor einem Jahr Letzter Beitrag vor einem Jahr 728 Views
P
perlfred Themenstarter:in
251 Beiträge seit 2010
vor einem Jahr
Type Variable als Type Angabe benutzen

Hallo!

Das ich keinen festen Type habe macht mich fertig!

Bezugnehmend auf meine letzte Frage: ValueConverter-Parameter Type übergeben ergibt sich jetzt noch ein Problem.

Ich möchte den ValueConverter in eine Bibliothek übernehmen. Diese hat als Zielframwork .NET Framework 4.5 sprich Sprachversion C# 7.3.
Bei dem Erstellen des Enum-Eintrages aus dem übergebenen Namen und dem übergebenen Type:


if (Enum.TryParse(type, name, out object enumValue))                                  // Konnte aus dem Enum-Type und dem Enum-Namen ein Enum-Eintrag erstellt werden?

wird mir der Fehler:

Fehlermeldung:
Fehler: CS8370 Das Feature "not-Muster" ist in C# 7.3 nicht verfügbar.

angezeigt.
Also habe (wollte) ich die TryParse-Methode der Version 7.3 benutzen.



Enum enumValue;
Enum.TryParse(name, out enumValue);


Dabei stieß ich auf das Problem, dass ich für die out-Variable eine Deklaration mit dem speziefischen Type der Enumeration benötige.> Fehlermeldung:

Fehler: CS0453 Der Typ "Enum" muss ein Non-Nullable-Werttyp sein, wenn er als TEnum-Parameter im generischen Typ oder in der generischen Methode "Enum.TryParse<TEnum>(string, out TEnum)" verwendet werden soll.

Mit type hätte ich zwar den spezifischen Type, aber den kann ich ja nicht als Variable casten.


Type type = (Type)parameter;  // Ziel Enum-Type "holen"

(type)enumValue;  // geht nicht

object enumValue = Activator.CreateInstance(type); // geht nicht, da objekt wieder nullable ist

Wie bekomme ich denn aus einer Type-Variable eine Type Angabe???

T
2.219 Beiträge seit 2008
vor einem Jahr

Warum verwendest du noch .NET 4.5?
Das hat seit einiger Zeit keinen Support mehr.
Am besten ist ein Update auf .NET 4.8, dann hast du solche und weitere Probleme nicht mehr.

Microsoft .NET Framework - Microsoft Lifecycle

Nachtrag:
Kontext für alle anderen.

ValueConverter-Parameter Type übergeben

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

16.807 Beiträge seit 2008
vor einem Jahr

Wird ja vielleicht seinen Grund haben so eine Steinzeit-Version zu verwenden, welche auch immer.

Der Fehlercode CS8370 ist leider ein Sammelcode, dahinter stecken dutzende von potentiellen Fehlern. In der Regel immer Fehler, die darauf schließen, dass man Sprachfeature verwendet, die aufgrund der Version nicht zur Verfügung stehen.
Die Lokalisierung des Fehlertexts ist fürchterlich; ich kann mir kein Reim draus machen, welche originale, englische Fehlermeldung hier gemeint sein könnte. Eine der vielen Gründe, wieso man mit lokalisierten SDKs nicht arbeiten sollte: Fehlersuche über Google endet meist ohne Treffer.
Aber wenigstens hast die Ursache gefunden.

Ein Enum ist keine klassische Instanz, sondern im Endeffekt "nur" ein "statischer" Programmierhelfer. Daher kann man logischerweise keine Instanzen von Enums erzeugen. Sie unterliegen auch nicht den Regeln von instantiierbaren Typen.
Das würde auch den Sinn von Enums völlig untergraben; gilt nicht nur für .NET so, sondern zB auch in C, C++....

Ich hab das kurz mit .NET 4.6 getestet (älter geht bei mir nicht mehr); funktioniert einwandfrei mit TryParse:


using System;

namespace ConsoleApp1
{
    public enum MyEnum { No, Yes }

    internal class Program
    {
        static void Main(string[] args)
        {
            string input = "no";
            MyEnum result;

            if (Enum.TryParse(input, true, out result))
            {
                Console.WriteLine("My Enum is: " + result); // Output: My Enum is: No
            }
            else
            {
                Console.WriteLine("Unable to parse: " + input);
            }
        }
    }
}

Dabei stieß ich auf das Problem, dass ich für die out-Variable eine Deklaration mit dem speziefischen Type der Enumeration benötige.

Das ist in der Form korrekt, und kannst Du ohne weiteres auch nicht ändern. Sie unterliegen halt anderen Regeln als Typen.
Hinter einem Type steckt eine Identität mit Namespace, Name etc etc... Ein Enum ist einfach nur ein int oder ein Byte. Ein Enum hat keine Identität, dessen Ursprung dafür verwendet werden könnte.

Hab Dein anderes Thema mal so überflogen; liest sich als ob ne Eierlegende Wollmilchsau umgesetzt werden soll. Da wirds mit Enums schwer.

P
perlfred Themenstarter:in
251 Beiträge seit 2010
vor einem Jahr

Hallo T-Virus!

Ich verwende diese Bibliothek in vielen meiner Projekte und habe (noch) nicht den Überblick was für Auswirkungen eine Veränderung haben kann.

Mir ist überhaupt der Zusammenhang zwischen .NET-Version und C#-Sprachversion nicht so ganz klar.

Ich weiß, dass ich theoretisch die Sprachversion (unabhängig von der .NET-Version???) in der VS-Projekt-Datei einstellen kann.

In der jetzigen Fehlermeldung wird eigentlich die C#-Sprachversion 9.0 vorgeschlagen.

Fehlermeldung:
Fehler: CS8370 Das Feature "not-Muster" ist in C# 7.3 nicht verfügbar. Verwenden Sie Sprachversion 9.0 oder höher.

Die Sprachversion C# 9.0 verlangt lt. (m)einer Übersicht eigentlich sogar die .NET 5.0 Version. Bei Umstellungen meiner Projekte auf .NET Core-Versionen, hatte ich allerdings schon masive Probleme mit verschiedenen NuGet-Paketen! Deshalb bin ich da jetzt etwas vorsichtiger geworden.

Wie kommst du überhaupt auf .NET 4.8? Die C# 8.0 Sprachversion beginnt ab .NET Core3.0.

Mal unabhängig von all diesen Zusammenhängen möchte ich natürlich auch mein Wissendefizit, was diese Grundlagen betrifft, reduzieren. Es muss ja auch vorher eine Lösung für dieses Problem (Enum.TryParase mit Variable als Type) geben!!!

16.807 Beiträge seit 2008
vor einem Jahr

Mir ist überhaupt der Zusammenhang zwischen .NET-Version und C#-Sprachversion nicht so ganz klar. [FAQ] Das .NET Ökosystem - .NET, .NET Core, .NET Standard, NuGet und Co

Wie kommst du überhaupt auf .NET 4.8? Die C# 8.0 Sprachversion beginnt ab .NET Core3.0. C# language versioning - C# Guide
Ja und Nein.

C# 8 wird von .NET Core und .NET Standard unterstützt. .NET Standard wiederum kannst Du auch in gewissen Versionen in .NET Framework verwenden.
.NET Standard

Deine .NET Version is aber sooo dermaßen alt, da ist nix mehr zu machen.

Es muss ja auch vorher eine Lösung für dieses Problem (Enum.TryParase mit Variable als Type) geben!!!

Nicht in der Form, weil Enums keine Types sind. Bei Enums ist immer notwendig, dass das Ziel-Enum bekannt ist.

2.078 Beiträge seit 2012
vor einem Jahr

Die C#-Version setzt keine .NET-Version voraus, einige Features dagegen schon, das sind häufig nur Compiler-Features, die bestimmte Klassen/Structs anhand Namespace und Namen suchen.
Viele Features kann man sich also "nachrüsten", indem man diesen Code anbietet, häufig gibt's dafür schon fertige NuGet-Packages, oder Du kopierst dir den Code selber.
Ob man das machen will/sollte, ist eine andere Frage, aber bei einfachem Code (z.B. die NullableReferenceType-Attribute) sehe ich da überhaupt kein Problem.

By the way: Enum.TryParse(Type, String, Object)
Das gibt's in .NET 4.6 (älter geht bei mir nicht) gar nicht, dann gibt's das in 4.5 auch nicht.

Besser wäre aber, Du bringst alle deine Projekte auf 4.8 und in deinem gemeinsam genutzten Projekt arbeitest Du mit .NET Standard 2.0 - .NET 6/7 wäre natürlich noch besser

P
perlfred Themenstarter:in
251 Beiträge seit 2010
vor einem Jahr

Hallo T-Virus!

Danke für deine Antwort!

Am besten ist ein Update auf .NET 4.8, dann hast du solche und weitere Probleme nicht mehr.

Leider ist dem nicht so. Ich habe meine Bibliothek jetzt auf die .Net Framwork-Version 4.8 geändert. Hier ist das Feature "not-Muster" auch noch nicht verfügbar. Scheinbar ist die Fehlermeldung:

Fehlermeldung:
Fehler: CS8370 Das Feature "not-Muster" ist in C# 7.3 nicht verfügbar. Verwenden Sie Sprachversion 9.0 oder höher.

doch präzise und das Feature ist erst ab der C# Sprachversion 9.0, das würde lt. der Zuordnungstabelle .Net 5.0 bedeuten, verfügbar!

Mit einer Erhöhung innerhalb der .Net Framework-Versionen hätte ich kein Problem. Ein Wechsel der Bibliothek zu den Core-Versionen erscheint mir aber doch recht aufwendig, da ich dann viele (nicht alle [die Neueren habe ich schon in Core 3.1 oder .Net 5/6/7 erstellt]) Projekte "hochziehen" müsste, da diese Bibliothek in fast allen Projekten benutzt wird.

Die Aussage von Palladin007:

Viele Features kann man sich also "nachrüsten", indem man diesen Code anbietet, häufig gibt's dafür schon fertige NuGet-Packages, oder Du kopierst dir den Code selber.

muss ich erst hinterfragen. Wie (woher?) kann ich den Code für ein Features nehmen, den ich kopieren könnte.

Die letzte Möglichkeit besteht natürlich darin, eine neue Bibliothek mit der passenden C# Sprachversion zu erstellen und diese jetzt und in der Zukunft zu verwenden.

2.078 Beiträge seit 2012
vor einem Jahr

Leider ist dem nicht so. Ich habe meine Bibliothek jetzt auf die .Net Framwork-Version 4.8 geändert. Hier ist das Feature "not-Muster" auch noch nicht verfügbar.

Stell auf Englisch um (ggf. reicht das bei Visual Studio?), ich kann mit der Fehlermeldung auch nichts anfangen.

muss ich erst hinterfragen. Wie (woher?) kann ich den Code für ein Features nehmen, den ich kopieren könnte.

Es gibt mehrere Quellen:
https://source.dot.net/
https://github.com/dotnet/runtime (Branches pro Version beachten)
https://www.nuget.org/ (Häufig gibt's das schon, z.B. "Nullable")

Was Du brauchst, musst Du allerdings selber herausfinden, z.B. mit https://sharplab.io/ (Rechts oben "Results" auf "C#" einstellen)

Die letzte Möglichkeit besteht natürlich darin, eine neue Bibliothek mit der passenden C# Sprachversion zu erstellen und diese jetzt und in der Zukunft zu verwenden.

Das wäre auch eine Idee.
Ggf. arbeitest Du mit einem Shared Project als "gemeinsamen Nenner", der den konkreten Code enthält und dann mit preprocessor directives (gibt's für die .NET-Versionen eine ganze Menge) entscheidest, welcher Code verwendet wird.
Es funktioniert und Visual Studio hilft auch gut dabei, aber mehr als eine Übergangslösung sollte das nicht sein.

P
perlfred Themenstarter:in
251 Beiträge seit 2010
vor einem Jahr

Hallo Abt!

Vielen Dank für deinen Link auf die Übersicht und Erklärung der Zusammenhänge und Entwicklung der .Net (Framework) Versionen.

Hast du (ihr) sehr verständlich erklärt! Hat mir geholfen!

Ich habe hier noch eine sehr anschauliche Grafik dazu gefunden.

Dass die Enum.TryParse<TEnum>(String, Boolean, TEnum)-Methode, wie in deinem Beispiel demonstriert, funktioniert, davon bin ich ausgegangen. Hier hast du aber den Ziel-Type (MyEnum) fest vorgegeben.

Ich such(t)e eine Enum.TryParse(Type, String, Object) Überladung, wie sie ab der C# Sprachversion 9.0 verfügbar ist, für ein .Net-Framework Projekt. Oder eine entsprechende Alternative, bei der man den EnumType als (TYPE) Variable angeben kann.

Ganz so fett ist meine WollMilchSau dann doch wieder nicht, ich möchte doch nur den Enum-Wert von einem Enum-Name eines beliebigen Enum-Typ's durch einen ValueConverter konvertieren.

Trotzdem Danke für deine Antwort.

16.807 Beiträge seit 2008
vor einem Jahr

ich möchte doch nur den Enum-Wert von einem Enum-Name eines beliebigen Enum-Typ's durch einen ValueConverter konvertieren.

Das geht so nicht, weil Enums nicht wie Types funktionieren.
Du kannst Enums nicht wie Types behandlen, auch wenn Du das gern hättest.

Bei Enums ist immer notwendig, dass das Ziel-Enum bekannt ist.

Du musst in Deinem Converter also dafür sorgen, dass das Ziel bekannt wird. Dann ist das kein Problem.

P
perlfred Themenstarter:in
251 Beiträge seit 2010
vor einem Jahr

Hallo Abt!

Ich glaube du vermutest, dass ich etwas Anderes möchte, als es der Fall ist!

Prinzipiell funktioniert meine Anforderung:

Ich möchte doch nur den Enum-Wert von einem Enum-Name eines beliebigen Enum-Typ's durch einen ValueConverter bestimmen.

Aus einem Enum-Name (name [String-Variable]) und einem Enum-Type (type [Type-Variable]) den Enum-Wert bestimmen:

Lösung von Palladin007:


if (Enum.TryParse(type, name, out var enumValue))
{
    if (Enum.IsDefined(type, enumValue)) return (int)enumValue;
}

Ein Value Converter auf Basis der C# 9.0 Sprachversion funktioniert (wie erwartet) anstandslos.

Es ist also nicht die Frage ob es möglich ist, sondern ob eine "Entsprechung" für die Enum.TryParse(Type, String, Object) Überlagerung aus der C# 9.0 Sprachversion
mit "Bordmitteln" aus C# in der Spachversion C# 7.3 emuliert werden kann ???

P
perlfred Themenstarter:in
251 Beiträge seit 2010
vor einem Jahr

Hallo Palladin007!

Vielen Dank für deine vielen Lösungsansätze!!!

Ich werde mich jetzt erst einmal ein "wenig" damit beschäftigen müssen...

2.078 Beiträge seit 2012
vor einem Jahr

Du könntest es dir auch auf umständliche Weise selber bauen.

Enum.GetValues() liefert dir alle Werte und Enum.GetName() liefert dir den Namen eines Wertes.
Mit den beiden Methoden müsstest Du dir den Wert anhand des Namens suchen können.

Aber schön ist das nicht und besser wäre, Du beseitigst den Grund, warum Du den Namen im ViewModel und den Wert in der View brauchst, das klingt nämlich etwas unsauber 😉
Ich kenne es eher andersherum, dass man im ViewModel das Enum hat und in der View den Namen oder eine Lokalisierung dafür.

Und ich hab das Gefühl, ein und das selbe Problem verteilt sich gerade auf zwei Themen, zumindest weiß ich nicht mehr, wohin mit meiner Antwort.

P
perlfred Themenstarter:in
251 Beiträge seit 2010
vor einem Jahr

Hallo Palladin007!

Du bist ja ein richtiger Fuchs! 🙂

Aber schön ist das nicht und besser wäre, Du beseitigst den Grund, warum Du den Namen im ViewModel und den Wert in der View brauchst

Dem ist natürlich nicht so. Das ist mehr dem EierlegendenWollMilchSau-Ansatz zu verdanken, da ich einen wirklich universellen ValueConverter erstellen will.

Sonst wäre es ja nur ein Einzeiler: (und Convert-Parameter bräuchte ich garnicht)


if (value != null && value.GetType().BaseType == typeof(Enum)) return (int)value;       // Wenn ein Enum-Eintrag übergeben wurde kann sofort der Enum-Wert zurückgegeben werden.

... das klingt nämlich etwas unsauber Danke für die wohlwollende Umschreibung. 😉

Enum.GetValues() liefert dir alle Werte und Enum.GetName() liefert dir den Namen eines Wertes.
Mit den beiden Methoden müsstest Du dir den Wert anhand des Namens suchen können.

Aber schön ist das nicht ...

Aber ein großer Aufwand ist es auch nicht und in diesem Fall erst einmal eine gute Lösung!!!! (Danke für die Steilvorlage!)


Enum myEnum = Activator.CreateInstance(enumType) as Enum;                     // Enum-Instanz erstellen
Array WerteListe = Enum.GetValues(enumType);                                            // Werte-Liste "holen"
foreach (var item in WerteListe)                                                                    // Werte-Liste iterieren
    if (Enum.GetName(enumType, item).Equals(name)) return (int)item;           // Wenn der Enum-Name vorhanden ist, Enum-Wert zurückgeben

Die statischen Enum-Methoden sind goldwert!

An dieser Stelle auch noch ein Danke!!! an Alle die sich mit dem/meinem Problem beschäftigt haben!!!

Hier noch der gesamte ValuConverter (für C# 7.3):


public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{

    if (value != null && value.GetType().BaseType == typeof(Enum)) return (int)value;       // Wenn ein Enum-Eintrag übergeben wurde kann sofort der Enum-Wert zurückgegeben werden.

    string name = value?.ToString();                                                        // Es wurde ein Enum-Name übergeben, diesen in String konvertieren

    if (name is null || parameter is null || (Type)parameter is null) return -1;            // Wurde zum Enum-Namen ein Enum-Type (im Parameter) übergeben? Nein -> Abbruch
    Type enumType = (Type)parameter;                                                        // Ziel Enum-Type "holen"
    if (!enumType.IsEnum) return -1;                                                        // Ist der Ziel-Type ein Enum-Type? Nein -> Abbruch
              
    Enum myEnum = Activator.CreateInstance(enumType) as Enum;                               // Enum-Instanz erstellen
    Array WerteListe = Enum.GetValues(enumType);                                            // Werte-Liste "holen"
    foreach (var item in WerteListe)                                                        // Werte-Liste iterieren
        if (Enum.GetName(enumType, item).Equals(name)) return (int)item;                    // Wenn Enum-Name vorhanden ist, Enum-Wert zurückgeben

    return -1;                                                                              // Ansonsten -1 für: Es konnte kein Enum-Wert ermittelt werden.
}


PS:

Sorry, wie bekomme ich denn den Namen des Verfassers in das Zitat?

2.078 Beiträge seit 2012
vor einem Jahr

Die myEnum-Variable ist immer noch überflüssig und Du nutzt sie auch nicht.


parameter is null || (Type)parameter is null

Das ist auch überflüssig, ob gecastet oder nicht, es ist immer null.

Lieber so:


Type enumType = parameter as Type;

if (enumType is null)
    return -1;

Wie ich es aber ganz gerne schreibe, weil ich dann weniger Einrückung habe:

[csharp ]
if (enumType is not Type enumType)
return -1;

// Hier hat "enumType" den korrekten Wert[/csharp]

Pattern matching wird aber auch hier thematisiert: [FAQ] Casten aber richtig: Boxing/Unboxing - () / is / as / Pattern Matching

Sorry, wie bekomme ich denn den Namen des Verfassers in das Zitat?

Indem Du "=perlfred" in die eckigen Klammern hinter das "quote" schreibst

P
perlfred Themenstarter:in
251 Beiträge seit 2010
vor einem Jahr

Hallo Palladin007!

Die myEnum-Variable ist immer noch überflüssig und Du nutzt sie auch nicht.

Man sollte schon die letzte Version posten 😮), (so etwas wird ja normalerweise vom VS schon "angemeckert"), aber ich hatte das automatisch korrigieren lassen (IntelliSense) und die sinnlose Anweisung:


_ = Activator.CreateInstance(enumType) as Enum;     // Enum-Instanz erstellen

ist mir später nicht mehr aufgefallen.

if ( ... parameter is null || (Type)parameter is null)

Was dabei gedacht hatte ich mir schon ... If-Abfragen werden doch von links nach rechts abgearbeitet ... also erst geprüft ob parameter nicht gleich null ist und wenn dem so ist wird der parameter gecastet ... Ich hatte "übersehen", dass wenn der parameter nicht gecastet werden kann eine Exception geworfen wird und nicht , wovon ich ausgegangen bin, null wird. Was man doch im Alltag so alles verkehrt macht! Vielen Dank! für dein korrektes Beispiel!

Dein zweites Beispiel benutze ich so auch immer häufiger (erspart ein extra casten), es ist aber erst ab der C#-Version 9.0 implementiert.