Laden...

Generics

Erstellt von Vinx vor 16 Jahren Letzter Beitrag vor 16 Jahren 1.949 Views
V
Vinx Themenstarter:in
38 Beiträge seit 2005
vor 16 Jahren
Generics

Hallo allerseits,

vielleicht hat hier jemand eine schöne Idee für mein Problem:

Ich habe einen Service, bei dem ich Werte in typisierter Form abfragen kann. Diese Typen können sehr viele werden (short, int, bool... etc. bestimmt 30 Stück).

Diese 30 Methoden möchte ich ungern mit in die Logik mitschleifen. Deshalb wollte ich mir eine Art Proxy bauen, in dem ich eine Methode habe, der ich den Typ mitgebe.

Hier erstmal exemplarisch der Service:


class Service
	{
		public void GetValue(ref int value)
		{
			value = 1000;
		}
		public void GetValue(ref bool value)
		{
			value = true;
		}
	}

Hier der Proxy:



class Proxy
	{
		private Service service;

		public Proxy(Service service)
		{
			this.service = service;
		}

		public T Get<T>()
		{
			T result;
			service.GetValue(ref result);
			return result;
		}
	}

Und hier noch, wie ich mir vorgestellt habe den Proxy zu verwenden:


class Generics
	{
		private Proxy proxy;

		public Generics()
		{
			proxy = new Proxy(new Service());
		}

		public void Test()
		{
			int number = proxy.Get<int>();
			Console.WriteLine(number);

			bool flag = proxy.Get<bool>();
			Console.WriteLine(flag);
		}
	}

Die Anwendung kann jetzt nicht mehr kompiliert werden, da: cannot convert from 'ref T' to 'ref int' was zwar nachvollziehbar, aber schade ist 🙂

Hat jemand hier eine Idee für mich?

Danke schon mal für Eure Antworten.

F
24 Beiträge seit 2007
vor 16 Jahren

Also das Problem ist ja, daß zu Compile-Zeit nicht klar ist, welchen Typ T in "public T Get<T>()" hat, da T erst zur Laufzeit bekannt ist.

Um Methoden eines Objektes zur Laufzeit aufzurufen gibt es die System.Reflection.MethodInfo Klasse.
An diese kommt du, wenn du in der Proxy Klasse vom Feld "service" die Methode getType() gefolgt von getMethod("GetValue") aufrufst.


MethodInfo getValue = service.getType().getMethod("GetValue");

Wenn du eine Methode überladen hast, wirst du mit diesem Aufruf eine Exception erhalten, da ja nicht genau bestimmt werden kann, welche Methode du haben willst.

Man kann getMethod(...) noch genauer sagen, welche Methode man haben will. Das kann aber unter Umständen zu einem ganz schönen Herrumgebastel werden.

Ansonsten sehe ich keinen Weg an einer If-Abfrage vorbei, da du ja auf jeden Fall erst zur Laufzeit den Typ von T kennst.

Gruß Fabian

V
Vinx Themenstarter:in
38 Beiträge seit 2005
vor 16 Jahren

Vielen Dank für Deine Antwort!

Wie es aussieht, komme ich dann wohl nicht um die 63 (brauch jede nämlich auch noch dreimal) Methoden drum herum. Bitter.

Naja, auf jeden Fall vielen Dank noch mal.

P
80 Beiträge seit 2007
vor 16 Jahren

Hallo Vinx,

so kann es nicht funktionieren, da du ja als generischen Parameter auch einen Typ angeben kannst für den garkeine Überladung für "GetValue" im Service existiert.

Ich sehe da zwei Lösungsmöglichkeiten:
a) Du machst die Methode "GetValue" im Service generisch. Dort müsstest du dann den Typ von T abfragen und einen Wert ausgeben. Wenn du den Service nicht ändern kannst oder darfst geht das natürlich nicht.

b) Du fragst in deinem Proxy den Wert von T ab und rufst die entsprechende Methode auf. Damit schleppst du aber wieder die Methoden in die Logik, was du ja nicht wolltest.

In einen saueren Apfel wirst du wohl beißen müssen. 😉

Grüße
Alex

2.921 Beiträge seit 2005
vor 16 Jahren

in gewisser Weise könntest Du auch

Interface zur Laufzeit zu einer Klasse hinzufügen (Lösung)

so etwas ähnliches benutzen.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

F
24 Beiträge seit 2007
vor 16 Jahren

Hatte eben noch eine Idee mit Operator-Überladungen. Vielleicht kannst du damit ja was anfangen.

class Program
    {
        static void Main(string[] args)
        {
            MyValue x = 1;
            Console.WriteLine((int)x);
            x = true;
            Console.WriteLine((Boolean)x);
        }
    }

    public class MyValue
    {
        private object m_value;
        protected MyValue(object value)
        {
            m_value = value;
        }

        public static implicit operator MyValue(int i)
        {
            return new MyValue(i);
        }

        public static implicit operator MyValue(bool b)
        {
            return new MyValue(b);
        }

        public static explicit operator int(MyValue v)
        {
            return (int)v.m_value;
        }

        public static explicit operator bool(MyValue v)
        {
            return (bool)v.m_value;
        }
    }

Für jeden Datentyp den du in MyValue abspeichern willst schreibst du dir einen entsprechenden implicit- und einen explicit-Operator.

Das

MyValue x = 1;
Console.WriteLine((int)x);

kann man ja fast deinem

int number = proxy.Get<int>();
Console.WriteLine(number);

gleich setzen. Du müsstest proxy umschreiben, daß deren Get-Methode immer einen Wert vom Typ MyValue zurück gibt.

Gruß Fabian

S
8.746 Beiträge seit 2005
vor 16 Jahren

Dein Beispiel (63 GetValue-Methoden?) ist so praxisfern, dass ich spontan dachte: Blödsinn. Aber vielleicht kann man die Motivation ja erkennen, wenn du das mal konkret machst. Ich vermute nämlich eher so eine Art Designfehler, der überhaupt erst den Wunsch nach sowas aufkommen läßt.

3.971 Beiträge seit 2006
vor 16 Jahren

Und wenn du sowas in der Art verwendest?


	class MyValue
	{
		public static T GetValue<T>(MyValue inst)
		{
			if (inst == null) throw new ArgumentNullException("inst");

			Type tp = typeof(T);

			//Sondertypen
			if (tp == typeof(bool))
			{
				object tmp = true;
				return (T)tmp;
			}

			else if (typeof(ValueType).IsAssignableFrom(tp) && inst.m_value == null)
			{
				throw new NullReferenceException();
			}
			else if (tp.IsAssignableFrom(inst.m_value.GetType()))
			{
				return (T)inst.m_value;
			}
			else
			{
				throw new NotSupportedException();
			}
		}

		private object m_value;
	}

Ansonsten wäre da noch der Vorschlag


class MyValue<T> {...}

zu verwenden

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

183 Beiträge seit 2004
vor 16 Jahren

Hallo Vinx,

irgendwie versteh ich dein Problem nicht.

Diese 30 Methoden möchte ich ungern mit in die Logik mitschleifen. Was verstehst du unter mitschleifen?

class Test
{
   private Service service;

   public Test()
   {
      service = new Service());
   }

   public void TestMe()
   {
      int number = 0;
      service.GetValue(ref number);
      Console.WriteLine(number);

      bool flag = false;
      Service.GetValue(ref flag);
      Console.WriteLine(flag);
    }
}

Sollte problemlos funktionieren.

Wenn du trotzdem eine generische Methode willst probier:

public T Get<T>()
{
   T result = default(T);

   object[] args = new object[] { result };

   //ParameterModifier[] modifiers = new ParameterModifier[] { new ParameterModifier(1) };
   //modifiers[0][0] = true;
   service.GetType().InvokeMember("GetValue", BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, service, args, /*modifiers*/ null, null, null);

   return (T)args[0];
}

So einfach wie möglich, aber nicht einfacher. [Albert Einstein]

take a look at
* baer-torsten.de
* codinghints

V
Vinx Themenstarter:in
38 Beiträge seit 2005
vor 16 Jahren

Hi,

vielen Dank für Eure zahlreichen Antworten. Gestern war ich den ganzen Tag nicht da, deshalb erst heute meine Antwort.

Konkret geht es um eine Datenschicht, die eine Klasse namens "Value" enthält. Dieser kann über den Kostruktor eine Wert übergeben werden. Der Konstruktor ist eben ca. 20 mal überladen (für int, uint, short, ushort, bool, ubool g, usw.). Weiter gibt es ca. 20 Get- und 20 Set-Methoden.

Dann gibt es noch drei verschieden Algorithmen für den Zugriff auf Skalar-, Array- und Listenwerte.

Das ist einfach so und daran kann ich auch nichts machen.

Naja, und dieses Ding möchte ich jetzt kapseln, sodass es keine Abhängikeit zur Datenschicht gibt.

Reflection ist aus Performancegründen keine Alternative. Boxing - hmmmm. Auch nicht so gut.

Deshalb dachte ich, ich könnte über die Generics gehen. Mir ist auch schon klar, warum das nicht funktioniert. Aber ich habe ja jetzt auch viele gute Anregungen von Euch bekommen. Danke dafür.

Ich werde mir jetzt noch mal in Ruhe ein paar Gedanken darüber machen...