none
Probleme mit generics und dynamic RRS feed

  • Frage

  • Hallo zusammen,

    bei der folgenden generischen Funktion gibt es beim Übersetzen Fehlermeldungen für Zeilen die 'var sum = (T)0' und 'sum += arr[i + n] * filter[n]' => "Eine Konvertierung vom Typ "int" in "T" ist nicht möglich." und "Der Operator "*" kann nicht auf Operanden vom Typ "T" und "T" angewendet werden." Soweit ich das gelesen habe, gibt es keine Möglichkeit mit 'where T : ...' T auf Wertetypen zu beschränken.

            public static T[] ConvolutionOn1DimArr<T>(T[] arr, T[] filter, int? centerIndex = null, T? borderValue = null) where T : struct
            {
                if (arr == null || filter == null)
                    return arr;
    
                if (centerIndex == null)
                    centerIndex = filter.Length / 2;
    
                if (centerIndex < 0 || centerIndex >= filter.Length)
                    throw new IndexOutOfRangeException("Das Zentrum des Filters liegt ausserhalb des gültigen Bereichs");
    
    
                var first = arr.Take(centerIndex.Value).Select(v => borderValue == null ? v : borderValue.Value);
    
                var last = arr.Skip(arr.Length - filter.Length + centerIndex.Value + 1).Select(v => borderValue == null ? v : borderValue.Value);
    
                var center = arr.Skip(first.Count()).Take(arr.Length - first.Count() - last.Count()).Select((v, i) =>
                {
                    var sum = (T)0;
    
                    for (int n = 0; n < filter.Length; n++)
                        sum += arr[i + n] * filter[n];
    
                    return sum;
                });
    
                return first.Concat(center).Concat(last).ToArray();
            }

    Ich bin dann auf den Datentyp 'dynamic' gestossen und habe meine Funktion wie folgt umgeschrieben:

            public static dynamic[] ConvolutionOn1DimArr(dynamic[] arr, dynamic[] filter, int? centerIndex = null, dynamic borderValue = null)
            {
                if (arr == null || filter == null)
                    return arr;
    
                if (centerIndex == null)
                    centerIndex = filter.Length / 2;
    
                if (centerIndex < 0 || centerIndex >= filter.Length)
                    throw new IndexOutOfRangeException("Das Zentrum des Filters liegt ausserhalb des gültigen Bereichs");
    
    
                var first = arr.Take(centerIndex.Value).Select(v => borderValue == null ? v : borderValue.Value);
    
                var last = arr.Skip(arr.Length - filter.Length + centerIndex.Value + 1).Select(v => borderValue == null ? v : borderValue.Value);
    
                var center = arr.Skip(first.Count()).Take(arr.Length - first.Count() - last.Count()).Select((v, i) =>
                {
                    var sum = (dynamic)0;
    
                    for (int n = 0; n < filter.Length; n++)
                        sum += arr[i + n] * filter[n];
    
                    return sum;
                });
    
                return first.Concat(center).Concat(last).ToArray();
            }

    Leider gibt es dann beim Übersetzen die Fehlermeldung, dass 'int[]' nicht in 'dynamic[]' konvertiert werden kann:

    var arr = new int[10];

    var filter = new int[3];

    ConvolutionOn1DimArr(arr, filter);

    Hat jemand eine Idee, wie ich aus der Nummer wieder rauskomme, ohne für jeden Datentyp (int, float, double) eine eigene Implementierung schreiben zu müssen?

    Gruß,

    Karsten



    Donnerstag, 5. April 2012 14:40

Antworten

  • Hallo Karsten,

    das grundsätzliche Problem liegt darin, dass es keine Operatoren für Generics gibt,
    siehe dazu Jon Skeet / Marc Gravell: http://www.yoda.arachsys.com/csharp/miscutil/usage/genericoperators.html

    Dies könnte ein besserer Ansatz sein, anstatt dynamic zu verwenden, womit der Code insgesamt als late binding arbeitet.

    Da Du keine Testdaten mitgeliefert hast und ich auf die schnelle keine produzieren konnte (wollte),
    wäre eine mögliche Zwischenlösung, dynamic nur dort einzusetzen, wo es notwendig ist.
    Das wäre hier die Berechnung für center:

                var center = arr.Skip(first.Count()).Take(arr.Length - first.Count() - last.Count()).Select((v, i) =>
                {
                    // ab hier dynamic ...
                    dynamic sum = default(T);
    
                    for (int n = 0; n < filter.Length; n++)
                        sum += (dynamic)arr[i + n] * filter[n];
    
                    return (T)sum;
                    // ... Ende dynamic
                });
    

    wobei das Ganze (ungetestet und somit) ohne Gewähr ist.

    Gruß Elmar

    Donnerstag, 5. April 2012 16:39
    Beantworter

Alle Antworten

  • Hallo Karsten,

    das grundsätzliche Problem liegt darin, dass es keine Operatoren für Generics gibt,
    siehe dazu Jon Skeet / Marc Gravell: http://www.yoda.arachsys.com/csharp/miscutil/usage/genericoperators.html

    Dies könnte ein besserer Ansatz sein, anstatt dynamic zu verwenden, womit der Code insgesamt als late binding arbeitet.

    Da Du keine Testdaten mitgeliefert hast und ich auf die schnelle keine produzieren konnte (wollte),
    wäre eine mögliche Zwischenlösung, dynamic nur dort einzusetzen, wo es notwendig ist.
    Das wäre hier die Berechnung für center:

                var center = arr.Skip(first.Count()).Take(arr.Length - first.Count() - last.Count()).Select((v, i) =>
                {
                    // ab hier dynamic ...
                    dynamic sum = default(T);
    
                    for (int n = 0; n < filter.Length; n++)
                        sum += (dynamic)arr[i + n] * filter[n];
    
                    return (T)sum;
                    // ... Ende dynamic
                });
    

    wobei das Ganze (ungetestet und somit) ohne Gewähr ist.

    Gruß Elmar

    Donnerstag, 5. April 2012 16:39
    Beantworter
  • Hallo Elmar,

    dein Tipp 'dynamic' nur dort zu verwenden, wo es notwendig ist, ist für mich die Lösung. Vielen Dank.

    Gruß Karsten

    Donnerstag, 5. April 2012 18:55

  • Für die "kritische" Zeile könntest Du auch ein Delegate hineinreichen, dass die Berechnung übernimmt:
    Dynamic is oft ein bisschen slow.

    public static T[] ConvolutionOn1DimArr<T>(Func<T, T[], T[], T> mathDelegate, T[] arr, T[] filter, int? centerIndex = null, T? borderValue = null) where T : struct
    {
    
    	//...
    
    	T sum = default(T);
    	for (int n = 0; n < filter.Length; n++)
    		sum = mathDelegate.Invoke(sum, arr[i + n], filter[n]);
    
    	//...
    
    }
    
    /* Aufruf: */
    
    var arr = new int[10];
    var filter = new int[3];
    Func<int, int[], int[], int> mathDelegate = (sum, f1, f2) => sum + (f1 * f2);
    
    ConvolutionOn1DimArr(mathDelegate, arr, filter);
    
    //oder
    
    var arr = new double[10];
    var filter = new double[3];
    Func<double, double[], double[], double> mathDelegate = (sum, f1, f2) => sum + (f1 * f2);
    
    ConvolutionOn1DimArr(mathDelegate, arr, filter);

    Gruß,
    Christoph



    Mittwoch, 11. April 2012 14:19