Autor Beitrag
BLJ
Hält's aus hier
Beiträge: 4



BeitragVerfasst: Do 24.05.07 05:46 
Hallo zusammen,

wie allseits bekannt ist, kann man Attribute leider nicht als abstract definieren.
Ich habe nun einen Fall, wo dies sehr nützlich wäre:

1. eine Abstract Basis Klasse 'AbstractMessage'.
Sie definiert:
ausblenden C#-Quelltext
1:
protected Messages _MessageID; //Message = Enumerator!					


...
ausblenden C#-Quelltext
1:
public MessageID { get { return _MessageID; } }					
.

Dazu gibt's dann (bis zu) 255 Child-Klassen, bspw.

ausblenden C#-Quelltext
1:
2:
3:
4:
public ConcreteMessage
{
 new public static const _MessageID = Messages.ConcreteMessage;
}



Gründe für diese Umsetzung:
-Es soll garantiert sein, dass jedes Child ein Attribut _MessageID haben soll, das Public ist.
Grund: Ich muss von aussen über Reflection drauf zugreifen. GGf. könnte ich den Reflection Teil auch in AbstractMessage ablegen, dann müsste es nur noch protected sein. Spielt aber eh keine Rolle.
-Es soll per Property 'MessageID' drauf zugegriffen werden können (eigentl. zwecks Information-Hiding, jetzt aber dank public Attribut eigentl. nuztlos)


Hat mir jemand einen konkreten Vorschlag wie ich das besser implementieren kann?
Ergo, wie kann ich sicherstellen, dass die Child-Klasse das Attribut Messages _MessageID implementiert
+ dieses aber eine static const ist?

Wenn ich eine static const in der AbstractMessage-Klasse implementiere, so ist diese gültig für alle Child-Messages, was dann super unnütz wäre.

Attribute kann man nicht abstract definieren...


es bleibt...?


in der Abstract Klasse eine abstrakte register-Methode definieren, die in jedem Child implementieren: Ziel: Soll in der AbstractMessage-Klasse (wäre dann halt nicht mehr ganz so abstract..) eine Liste (static Attribt der Abstract Classs) definieren, welche MsgIDs und Child-Messages miteinander verknüpft? Das wäre ja gottlos umständlich für ein prinzipiell so simples Problem.

bessere Ideen sind gesucht...

Grüsse,
BLJ

Moderiert von user profile iconraziel: C#-Tags hinzugefügt
Robert_G
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 416


Delphi32 (D2005 PE); Chrome/C# (VS2003 E/A, VS2005)
BeitragVerfasst: Do 24.05.07 11:32 
Statische Teile einer Klasse können weder virtuell noch abstrakt sein.
Sie werden ja nicht auf eine bestimmte Instanz ausgeführt und somit wäre es unmöglich eine bestimmte Implementierung zu finden.

Was du dir da überlegt hast, wirkt IMHO nicht so glücklich...
1: Ist es notwendig, dass das Feld statisch ist?
2: Öffentliche Bezeichner, die durch einen Unterstrich entstellt sind, verstoßen gegen die .Net API Guidelines. ;-)
3: Reflection ist so ziemlich die langsamste Art einen Wert abzufragen.

Um dir wirklich helfen zu können, müsste ich mehr über das wissen, was du da vorhast.
Eine Klasse, die nur dafür da ist einen String zu liefern ist ja sicherlich nur etwas Glue-Code für ein IOC-basiertes Design.
Erzähle uns etwas mehr darüber wofür die die MessageId benutzt und vielleicht finde wir einen Weg, der den Einsatz von Reflection minimiert und/oder einen Weg für dich findet diese IDs zu erzwingen...

btw: Felder in .Net als "Attribute" zu bezeichnen ist etwas gefährlich, da es bereits eine Objektgruppe gibt, die "Attribute" heißt. ;-)
BLJ Threadstarter
Hält's aus hier
Beiträge: 4



BeitragVerfasst: Do 24.05.07 14:02 
Hi Robert,

danke für deine Antwort!

Die Messages sollen auf dem Com-Port verschickt werden.

Anhand der Message ID wird beim eintreffen ein Objekt erstellt. Momentan implementier ich in jeder Message à la:

ausblenden C#-Quelltext
1:
public static final _MessageID = Messages.TestMessage;					


public nur, da ich bei der Anwendungsinitialisation einmal mit Reflection alle Types mit .BaseClass = AbstractMessage durchgehe und diese so in meiner 'MessageFactory' (es ist kein echtes Factory Pattern, aber der Name passt ;-) ) registriere (mit MessageID + Konstruktor).

Ich möchte mehrere bspw. TestMessage : AbstractMessage erstellen können. Zum einen, weil ich darin zwei Konstruktoren habe (einen, um eine Nachricht, die gesendet werden soll, zu generieren, den anderen, um eine Nachricht, die angekommen ist zu 'erstellen'), anderseits weil ich die Messages ggf. in eine Queue setzen möchte.

Da ich eine geschlossene Einheit TestMessage habe, die sich die gemeinsame konstante Eigenschaft MessageID teilt, ist es nur logisch eine static final implementieren zu wollen.

Natürlich würde es auch ohne static gehen, nur; das wäre schlechtes OO-Design...

ich muss lediglich noch garantieren, dass diese Eigenschaft implementiert wird.
Das müsste ich sonst noch über ein seperates Interface IMessage machen?

ausblenden C#-Quelltext
1:
2:
3:
4:
public Interface IMessage
{
 Messages _MessageID;
}


?

Das ist aber auch keine LKösung, da ich dann in jeder child-Klasse à la
ausblenden C#-Quelltext
1:
TestMessage : AbstractMessage, IMessage					

implementieren muss.
Asonsten ist die Message wieder auf Ebene Abstractmessage; womit wir wieder am Anfang sind.

sonstige Ideen?
Robert_G
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 416


Delphi32 (D2005 PE); Chrome/C# (VS2003 E/A, VS2005)
BeitragVerfasst: Do 24.05.07 17:18 
Brauchst du wirklich statische Member?
Wenn eine Klasse statischen Status braucht, gibt es dafür entweder wirklich triftige Gründe, oder man verbaut sich damit sinnlos Skalierbarkeit.

Hier ist mal eine einfache Idee,wie es gehen könnte:

ausblenden Chrome-Quelltext
1:
2:
3:
4:
5:
6:
7:
var context := new MessageContext();

context.RegisterMessagesFromAssembly(typeof(ConsoleApp).Assembly);

var message := context['miep!'];

Console.WriteLine(message.DoSomething());


ausblenden Chrome-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
type
  IMessage = public interface
    property MessageId : String read;

    method DoSomething : String;
  end;

  HelloMessage = public sealed class(IMessage)
  public
    property MessageId : String 
      read 'miep!';

    method DoSomething : String;
  end;
  
implementation

method HelloMessage.DoSomething : String
begin
  exit 'hello';
end;



ausblenden volle Höhe Chrome-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
uses
  System.Collections.Generic;
  
type
  MessageContext = public class
  public
    property Item[messageId : String] : IMessage
      read MessagesById[messageId]; default;

    method RegisterMessage<T>(messageId : String);
      where T is IMessage, T has constructor;

    method RegisterMessage<T>(message : T);
      where T is IMessage;
    method RegisterMessagesFromAssembly(assemblyRef : System.Reflection.Assembly);
  private
    method RegisterMessage(messageId : Stringmessage : IMessage);

    MessagesById : Dictionary<String, IMessage> 
      := new Dictionary<String, IMessage>(); readonly;
  end;
  
implementation

method MessageContext.RegisterMessage<T>(messageId : String); 
begin
  RegisterMessage(messageId, new T());
end;

method MessageContext.RegisterMessage<T>(message : T); 
begin
  RegisterMessage(message.MessageId, message);
end;

method MessageContext.RegisterMessage(messageId : Stringmessage : IMessage); 
begin
  if not assigned(messagethen
    raise new ArgumentNullException('blblablbala...''message');

  if string.IsNullOrEmpty(messageId) then
    raise new ArgumentNullException('blblablbala...''messageId');

  if MessagesById.ContainsKey(messageId) then
    raise new ArgumentException('Message Id "' + messageId + '" is already blblablabla...',
                                'messageId');
  MessagesById.Add(messageId, message);
end;

method MessageContext.RegisterMessagesFromAssembly(assemblyRef : System.Reflection.Assembly); 
begin
  var messageType := typeof(IMessage);

  for clrType in assemblyRef.GetExportedTypes() do
  begin
    if not clrType.IsAbstract and messageType.IsAssignableFrom(clrType) then
      with message := Activator.CreateInstance(clrType) as IMessage do
        RegisterMessage(message.MessageId, message);
  end;
end;



Zur Pflicht der (Re-)Implementierung der Property:
Geht nicht!
Du kannst nicht wirklich prüfen, ob eine Klasse wirklich ihre eigene Version der Eigenschaft mitbringt.
OK, du könntest per Reflection prüfen, was der Declaring type der Property ist, aber das wäre schon arg krank. :shock:
Gehe einfach davon aus, dass derjenige, der solche Messages implementiert auch will, dass sie funktionieren. ;-)
BLJ Threadstarter
Hält's aus hier
Beiträge: 4



BeitragVerfasst: Do 24.05.07 17:31 
oh... danke für deine Mühe!
interessanter Weg. Ich muss zwar zugeben, dass ich etwas Mühe habe den Code zu lesen. Dünkt mich ein seltsames Konstrukt..
hab ja schon Visual Basic (+Script), C#, Java, C, C++, Javascript gesehen aber sowas...? nö...

Allerdings muss ich sagen, dass das schon ein ganz schön grosses Stück code für so eine einfache Funktionalität ist!

bezgl. Skalierbarkeit statische Variablen:
haben statische Variablen einen merkbaren negativen Performance-Einfluss?




Moderiert von user profile iconChristian S.: Weitere Fragen entfernt
Robert_G
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 416


Delphi32 (D2005 PE); Chrome/C# (VS2003 E/A, VS2005)
BeitragVerfasst: Do 24.05.07 17:47 
user profile iconBLJ hat folgendes geschrieben:
oh... danke für deine Mühe!
interessanter Weg. Ich muss zwar zugeben, dass ich etwas Mühe habe den Code zu lesen. Dünkt mich ein seltsames Konstrukt..
hab ja schon Visual Basic (+Script), C#, Java, C, C++, Javascript gesehen aber sowas...? nö...
Lol! Sorry hatte gerade keine Lust auf C#. ;-)

Zitat:
Allerdings muss ich sagen, dass das schon ein ganz schön grosses Stück code für so eine einfache Funktionalität ist!
Eigentlich nicht, den Großteil schreibt ja die IDE für einen. :mrgreen:

Zitat:
bezgl. Skalierbarkeit statische Variablen:
haben statische Variablen einen merkbaren negativen Performance-Einfluss?
Nein, aber sie existieren nur einmal pro AppDomain.
Das heißt du kannst dich ganz schön schnell selbst in den Fuß schießen, falls du mal 2 davon auf einmal brauchst. ;-)

Zitat:
leider habe ich im Moment andere (grössere) Probleme...
Neue/Andere Frage, neuer Thread.
Das geht sonst a) drunter und drüber und b) wird sich ein potentieller Helfer von meinem Schnipsel obendrüber abgeschreckt fühlen, bevor er deine VS-Frage sieht. :mrgreen: