Design-Frage



  • Hi Leute,

    wie würdet ihr folgendes lösen:
    Ich habe eine Funktion, die ist überladen:

    bool Compare(Path path1, Path path2); // vergleicht zwei Dateien
    bool Compare(string data1, string data2); // vergleicht die übergebenen Daten
    

    Dann habe ich ein XML-File, das das Verhalten der Anwendung vorgibt:

    <action name="Compare">
        <parameter name="First"><file>C:\test1.bin</file></parameter>
        <parameter name="Second"><file>C:\test2.bin</file></parameter>
    </action>
    

    bzw.

    <action name="Compare">
        <parameter name="First"><data>somedatahere</data></parameter>
        <parameter name="Second"><data>someotherdatahere</data></parameter>
    </action>
    

    Ich wollte es jetzt irgendwie so machen:

    Compare(ParameterFactory::CreateParameter(XML.parameter[0]).getInnerData(),XML.parameter[1]).getInnerData());
    

    Ich dachte mir jetzt, CreateParameter gibt mir einen spezielle Parameterklasse zurück (von einer abstrakten Parameter-Basisklasse abgeleitet) und getInnerData liefert dann was vom Typ string bzw Path zurück, je nachdem, was genau eben im parameter-tag steht.
    Leider tut das nicht, weil ne überladene Funktion natürlich den selben rückgabewert haben muss.
    Mit generics wollte ich es auch machen, aber das geht genausowenig:
    CreateParameter kann nciht ParameterBase zurückgeben, sondern die Definition dieser Funktion muss acuh schon nen Template-Parameter besitzen für ParameterBase.
    Wie mach ich das jetzt am klügsten und schönsten?



  • Servus,

    meine Idee dazu:

    Bau dir doch eine Collection. Da flummst du dann alle Items, die etwas bestimmtes machen sollen rein:

    Items Klasse

    using System;
    using System.Collections;
    
    namespace ActionHandler
    {
    	public class ActionItems : CollectionBase
    	{
            #region Constructor
    
            public ActionItems()
            {
    
            }
    
            #endregion //Constructor
    
            #region Properties
    
            public new int Count
            {
                get
                {
                    return base.Count;
                }
            }
    
            public ActionItem this[int index]
            {
                get  
                {
                    return ((ActionItem) ((IList)this)[index]);
                }
                set  
                {
                    ((IList)this)[index] = value;
                }
            }
    
            #endregion // Properties
    
            #region Methods
    
            public bool Contains(ActionItem value)  
            {
                return (((IList)this).Contains(value));
            }
    
            public int IndexOf(ActionItem value) 
            {
                return ((IList)this).IndexOf((object) value);
            }
    
            public void Remove(ActionItem value)  
            {
                ((IList)this).Remove(value);
            }
    
            public int Add(ActionItem value)  
            {
                return (((IList)this).Add(value));
            }
    
            public void Insert(int index, ActionItem value) 
            {
                ((IList)this).Insert(index, (object) value);
            }
    
            public new void Clear()
            {
                base.Clear();
            }
    
            public void CopyTo(ActionItem[] array, int index)
            {
                ((ICollection)this).CopyTo(array, index);
            }
    
            #endregion // Methods
    	}
    }
    

    Item Klasse

    using System;
    
    namespace ActionHandler
    {
    	public class ActionItem
    	{
            #region Fields
    
            private string fName;
            private Type fDataType;
            private object fValurFirst;
            private object fValurSecond;
    
            #endregion // Fields
    
            #region Properties
    
            public string Name
            {
                get
                {
                    return this.fName;
                }
                set
                {
                    this.fName = value;
                }
            }
    
            public Type DataType
            {
                get
                {
                    return this.fDataType;
                }
                set
                {
                    this.fDataType = value;
                }
            }
    
            // usw.
    
            #endregion // Properties
    
            #region Constructor
    
    	public ActionItem()
    	{
    
    	}
    
            #endregion // Constructor
    	}
    }
    

    Jetzt gehste hin und definierst jedes Item:

    Name: Compare
    DataType: Type.String
    ValueFirst: "BlaBlubb"

    Das flummste mit Add in die Collection.

    Bevor du jetzt anfängst irgendwie in einer XML Datei von Hand rumzuparsen, benutz einen XMLSerializer:

    using System;
    using System.IO;
    using System.Windows.Forms;
    using System.Xml;
    using System.Xml.Serialization;
    
    namespace ActionHandler
    {
    	public sealed class StandardSerializer
    	{
            #region Constructor
    
    		private StandardSerializer()
    		{
    
    		}
    
            #endregion // Constructor
    
    		#region Methods
    
    		// Path.GetDirectoryName(Application.ExecutablePath) + "\\Config.xml")
    		public static bool WriteAvailableData(string fileName, object item)
    		{
                if (item == null)
                    return false;
    
    			XmlSerializer xmlSerializer = new XmlSerializer(item.GetType());
    
    			try
    			{
    				using (TextWriter writer = new StreamWriter(fileName))
    				{
    					xmlSerializer.Serialize( writer, item );
    					writer.Close();
    				}
    
    				return true;
    			}
    			catch
    			{
    				return false;
    			}
    
    		}
    
    		public static object ReadAvailableData(string fileName, Type type)
    		{
                if (!new FileInfo(fileName).Exists)
                    return null;
    
    			XmlSerializer xmlSerializer = new XmlSerializer(type);
    
    			try
    			{
    				object serializedItem = null;
    
    				using (FileStream fileStream = new FileStream(fileName, FileMode.Open))
    				{
    					XmlReader xmlReader = new XmlTextReader(fileStream);
    
    					serializedItem = (object) xmlSerializer.Deserialize(xmlReader);
    
    					xmlReader.Close();
    					fileStream.Close();
    				}
    
    				return serializedItem;
    			}
    			catch
    			{
    				return null;
    			}
    		}
    
    		#endregion // Methods
    	}
    }
    

    Nachdem du die Items geschrieben hast, lädst du den Rotz einfach:

    ActionItems items = (ActionItems )StandardSerializer.ReadAvailableData(Path.GetDirectoryName(Application.ExecutablePath) + "\\CachedItems.xml", typeof(ActionItems));
    

    Damit wäre die Sache schonmal erledigt. Jetzt schreibst du noch einen Interpreter für die Items. d.h. du schaust nach was ein Item definiert und reagierst darauf.

    Du stehst jetzt mit meiner Lösung nur noch ein bisschen im Konflikt.. du musst es auf C++.Net umrotzen. 😉

    Aber so würde ich es machen..

    mfg
    Hellsgore



  • Hellsgore schrieb:

    Du stehst jetzt mit meiner Lösung nur noch ein bisschen im Konflikt.. du musst es auf C++.Net umrotzen. 😉

    Ich machs acuh mit C#.

    Danke für deine ausführliche Antwort, versuche sie jetzt zu verstehen 🙂



  • OK, so weit so klar.
    Im oberen Beispiel hätte ich also dann ne ActionItems-Collection mit zwei Items drin.
    Im ersten Fall:
    Item1.Name="First", Item1.DataType=Path, Item1.Value="C:\test1.bin"
    Item2.Name="Second", Item2.DataType=Path, Item2.Value="C:\test2.bin"
    Im zweiten Fall:
    Item1.Name="First", Item1.DataType=string, Item1.Value="somedatahere"
    Item2.Name="Second", Item2.DataType=string, Item2.Value="someotherdatahere"

    Wenn ich das grad richtig seh, macht dein Code es ein bisschen anders.

    Jedenfalls geht es jetzt darum, die Funktion aufzurufen. Geht folgendes?

    Compare(items[0].Value as items[0].DataType, items[1].Value as items[1].DataType);
    


  • Nein, geht nicht. Wie dann?



  • Wenn ich Dir richtig verstehe, ist das ein Castingproblem, oder?

    Wenn Du den DataType auswertest, kannst Du ja den Value-Inhalt (String oder Path etc.) entsprechend casten und dann die Methode mit den gecasteten Parametern aufrufen, oder geht es Dir darum, nur einen Methodenaufruf zu codieren, der sowohl Path als auch String annimmt? Geht denn letzteres?

    Das wäre doch dann eher ein Fall von variabler Anzahl Parameter...? Bzw. Du übergibst nur Objekte und sagst der Methode in einem weiteren Parameter, was für eine Art Objekt es ist, damit die Methode intern casten kann...?



  • Ich will ein "runtime-casting" machen, wenn man das so bezeichnen kann..
    Klar, ich könnte folgendes machen:

    if(items[0].Type==typeof(Path) && items[1].Type==typeof(Path))
        Compare(items[0].Value as Path, intems[1].Value as Path);
    else if(items[0].Type==typeof(string) && items[1].Type==typeof(string))
        Compare(items[0].Value as string, intems[1].Value as string);
    

    Aber das ist alles andere als schön und genau deswegen will ich es vermeiden.



  • Was gefällt Dir denn an der "Object"-Vorgehensweise nicht? Das entspräche ja dem Void bei C/C++ und es wäre das Verfahren, daß man in solchen Situationen anwenden würde. Und dabei handelt es sich ja um ein Runtimecasting...?



  • Nein, das ist einfach unschön. Was du mit void meinst, weiss ich nciht. Hinter Polymorphismus steckt ja gerade, dass man sowas nciht mehr machen muss.



  • Ich mach zu dieser Frage mal nen neuen Thread auf.



  • Servus,

    ich glaube du denkst dort etwas zu kompliziert. Ich behaupte jetzt einfach mal, dass es nicht gehen wird, was du vorhast. Dann würde ich dir in dem Fall einfach raten einen Switch-Case Block zu machen und auf den Datentyp zu achten.

    Mich würde jetzt genauer interessieren, was die Methode Compare macht. Willst du einfach die Inhalte der Items überprüfen bzw. vergleichen, ob sie gleich oder ungleich sind, dann kannste das so machen:

    ActionItem item = new ActionItem();
    item.Name = "Compare";
    item.DataType = typeof(string);
    item.Values = new object[] {"Test1", "Test2"};
    
    int comp = Comparer.DefaultInvariant.Compare(item.Values[0], item.Values[1]);
    
    //Less than zero = a is less than b. 
    //Zero = a equals b. 
    //Greater than zero = a is greater than b.
    

    mfg
    Hellsgore



  • Nein, will ich nicht. Es wird ein etwas komplizierterer Vergleich.

    Ich habe auch das Gefühl, dass das nicht geht, was ich will... Siehe dazu auch meinen anderen Thread...


Anmelden zum Antworten