Laden...

const Variable in Generics verwenden

Letzter Beitrag vor 14 Jahren 10 Posts 1.439 Views
const Variable in Generics verwenden

Hi

in C++ kann man mit Templates so was machen:


class A
{
  public:
    static const std::string dll_name;
};

const std::string A::dll_name("Test.dll");

template<typename Type>
class B
{
  public:
    static const std::string name;

    B()
    {
      // mach was
    }

    const std::string GetName() const {return name;}
};

template<typename Type> const std::string B<Type>::name = Type::dll_name;

main()
{
  B<A> xB;

  std::string s = xB.GetName();
  
}

kann man das iwie in C# Generics auch machen? Z.B.:


public class A
{
  public const String DLLName = "Test.dll";
}

public class B<Type>
{
   [DllImport(Type.DLLName)]
   public static extern void foo();
   
   ...
}

mfg
Tobias

Nein sowas geht nicht. Dem Generic B müssen beim kompilieren alle verwendeten Instanz-Funktionen bekannt sein. Du könntest das ganze beispielsweise mit einem zusätzlichen Interface auch in C# abbilden (where-Clause). Kannst diese allerdings dann nicht in mit Attributen verwenden.

Für deinen Fall wäre aber das Strategy-Pattern besser geeignet als Generics:



class Foos {
  [DllImport("Test1.dll")]
   public static extern int foo1(int arg1, int arg2);

  [DllImport("Test2.dll")]
   public static extern int foo2(int arg1, int arg2);
}

class B {
  public static Func<int, int, int> FooFunc { get; set; }

  void Bar() {
    int ret = FooFunc(1, 2);
  }
}

//Verwendung:
void Main() {
  if (...) {
    B.FooFunc = Foos.foo1;
  }
  else {
    B.FooFunc = Foos.foo2;
  }
}


Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

Das sieht schon mal nicht schlecht aus. Allerdings ist Func erst ab .NET 3.5 vorhanden 😦 Die Generics sind ein bisschen schwach finde ich... bin da von C++ verwöhnt 😉

die Func-Delegaten kannstedir für Framewok2 auch selbst deklarieren



public delegate T2 Func<T1,T2>(T1 p1);

etc.

Der frühe Apfel fängt den Wurm.

Generics sind zugegebenermaßen nicht so mächtig wie C++-Templates, aber den von dir geplanten Fall kannst du auch anders umsetzen, nämlich via ModuleBuilder.DefinePInvokeMethod(). Definiere dir die Methode zur Laufzeit in deine Anwendung. Der Nachteil sei aber nicht verschwiegen: So eine Methode kannst nur via Reflection aufrufen.

Wenn es nur darum geht zwei DLL-Varianten zu unterscheiden (z.B. für 32 und 64 bit), dann ist die simple Unterscheidung wie oben beschrieben deutlich einfacher.

Du kannst den Func-Delegate auch in .NET 2.0/3.0 selber definieren:


public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2);

Die Action<> und Func<>-Delegates sind einfach in .NET 3.5 schon vordefiniert (mehr nicht -)

Generell setzt man Generics meist immer mit passenden Interfaces bzw. Basisklassen ein (und somit eine logische Typsicherheit bieten, anstatt wie bei den Templates in C++ "nur" eine auf Namen/Operatoren basierende).

Jedoch hat dein Problem mit den Attributen nichts mit Generics zu tun.
Auch Member-Variablen funktionieren nicht innerhalb von Attributen:


public class bar
{
    string dll = "Test.dll";

    [DllImport(dll)] // <- Compiler-Fehler
    public static extern void foo();
}

@Cat


public class bar
{
    const string dll = "Test.dll";

    [DllImport(dll)]
    public static extern void foo();
}  

const ... und schon gehts 😃 ... aber danke für die Func definition!

mit C++ Templates hat man sehr wohl Typsicherheit. Man kann unter anderem dadurch sogar zur kompilelaufzeit berechnungen durchführen (Expression Template), Template spezialisierungen (partielle template spezialisierung) implementieren bzw. typen vom compiler erzeugen lassen. Bei Generics muss man ein Interface mit where nur angeben um überhaubt auf methoden von dem generic type zugreifen zu können bzw. man hat damit zur Programierzeit schon sicherheit das z.B. keine nicht vorhandene Methode aufgerufen wird. Bei C++ meckert der Compiler so was halt zur Kompilelaufzeit an.

ModuleBuilder.DefinePInvokeMethod scheint eine gute Sache zu sein. Einfache Funktionen aus einer DLL kann ich aufrufen. Aber bei Funktionen mit komplexen Parametern krachts noch:


C++

struct StructA
{
  int iLength;
  const char* pszBuffer;
};

struct StructB
{
  int iCount;
  StructA* pStructA;
};

__declspec(dllexport) const int __stdcall Foo(const StructB& xParams);


    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct StructA
    {
        public int iLength;
        [MarshalAs(UnmanagedType.LPStr)]
        public String sBuffer;
    };

    [StructLayoutAttribute(LayoutKind.Sequential)]
    public struct StructB
    {
        public int iCount;
        public IntPtr pStructA;
    };

Beim Aufruf der Methode Invoke für die Funktion Foo kommt es zu der Exception:
System.Reflection.TargetInvocationException {"Exception has been thrown by the target of an invocation."}

Ich initalisiere ModuleBuilder.DefinePInvokeMethod wie hier beschrieben:
http://msdn.microsoft.com/en-us/library/628csc41.aspx

Hat einer eine Idee?

Der Unterschied zu C++ Templates liegt daran, dass das Generika Foo<> immer kompiliert vorliegt. Um in C++ Templates verwenden zu können, muss der Quelltext vorliegen

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

Hallo tubias,

die eigentliche Fehlermeldung bei einer TargetInvocationException findest du in der Eigenschaft 'InnerException'.