Egentligen har jag två frågor där huvudfrågan är den som jag framförallt behöver hjälp med. Hej, Japp. Dessutom fick jag redan på att den webtjänst jag hade tänkt att använda kommer inomkort i ny version som gör mitt jobb enklare. Hej, Bra och subtil input... Om jag fattat det rätt så behöver du inga nästlade klasser för att göra det du vill. Skapa bara hjälpklasserna (underklasserna) vid sidan om varandra (i egna eller separata källkodsfiler) i samma projekt med samma namespace. Skulle du kunna ge ett snabbt exempel på hur ett sånt här alternativ där man bara "visar" uppräknaren utåt? Jag har försökt lite men får inte till det med vilken typ osv som Propertyn skall ha, och hur man initierar foreach utifrån en enumerator... Här kommer ett litet exempel: Kanon den där docnumber as IE... i Return parametern där hade tagit en stund att komma på själv. Ja:C# influerad arkitekturfråga
Jag har en applikation som skall hantera XML-dokument.
Grunden är:
Man skickar in ett XML-dokument till en Webtjänst och man får tillbaka ett XML-dokument som svar.
Jag tänkte då att jag skulle försöka bygga ett så heltäckande klassbibliotek som möjligt för att hantera informationen som hanteras till och från webtjänsten.
Arkitekturen jag tänkte då var:
En Generell klass hanterar hela dokumentet.
I det generella dokumentet kan det finnas taggar som är likadana, med motsvarande information i. Jag lägger då ett property av typen "ArrayList" i den generella klassen.
ett enkelt exempel:
private ArrayList _objInquiry;
public clsDocumentInquiryResponse(System.Xml.XmlDocument xmlDocumentInquiry)
{
XmlNodeList InquiryNodes = xmlDocumentInquiry.GetElementsByTagName("INQUIRY");
ArrayList InqElements = null;
foreach (XmlNode xnInquiry in InquiryNodes)
{
clsInquiry objInquiry = new clsInquiry(xnInquiry);
InqElements.Add(objInquiry);
}
this.Inquiry = InqElements;
}
public System.Collections.ArrayList Inquiry
{
get
{
return _objInquiry;
}
set
{
_objInquiry = value;
}
}
Som ni ser i exemplet så skapas det för varje nod, ett nytt objekt, som isolerar dokumentet mer och mer neråt i hierarkin. Poängen, tänker jag är då att det ensamma generella dokumentet har ju all information om hela xmldokumentet.
---Jag är ny på C# och XML - kanske finns det ett färdigt sätt att hantera detta?
Hursomhelst. Hur skall jag sedan bära mig åt för att nå informationen från det generella objektet?
jag vill ju typ kunna ange:
GenerelltObjekt.klass.Property
GenerelltObjekt.klass.klass.Property osv. beroende på vad jag vill ha ut av det ankommande dokumentet.
Hur görs detta? Eller görs detta på detta sättet rent arkitekturmässigt? Hur gör man annars?
----------
En annan fråga i parantesen är:
Kan man göra en villkorssats beroende på namnet av en variabel eller metod?
isf Hur?
//Fredrik L
Sv: C# influerad arkitekturfråga
Det finns redan många funktioner för att hantera XML dokument i C#. Titta på XmlDocument, XmlNode, XmlNodeList osv. Möjligen finns där redan vad du behöver. Du kan tex få ut en lista med alla noder av samma typ med XmlNode.SelectNodes("Namn"). Varje nod kan sedan innehålla attribut och andra noder.
Ett tips är att göra lite testkod och köra den i debuggern och kolla på de olika Xml objektens fält. Då får man rätt bra koll på vad de innehåller och vad man kan göra med dem.
Det finns även mycket funktioner för att validera ett xml dokument emot ett givet schema.
Om du är ny på C# så ska du också titta på collections (listor och arrays) och hur du stegar igenom dessa med foreach. Det gäller inte bara för XML.
För att göra en vilkorssats av namnet på en funktion så kan du använda funktioner från System.Reflection namespacet. Där kan du få ut namnet på funktionen som en sträng. Möjligen fungerar detta också med variabler (i alla fall på objekt baserade variabler) men det vet jag inte säkert. Varför vill du göra det?
/RubenSv:C# influerad arkitekturfråga
Hursomhelst... Jag fick principen att funka som jag tänkte mig i en enkel testapplikation.
Men skulle väldigt gärna ha feedback på koden och om det finns andra bra/bättre sätt att lösa det på?
(exempel där input inte är xml... i det här fallet)
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
namespace esptest
{
public class DocNumber
{
private ArrayList docnumber = new ArrayList();
enum Doc
{
CountryPart = 0,
NumberPart = 1
}
public DocNumber(string InputString)
{
//Lite olika medlemmar som skall sköta uppdelningen av en textsträng.
char[] sep1 = new char[] {','};
char[] sep2 = new char[] { ';' };
//splittar upp strängen vid varje kommatecken.
string[] colldocNumb = InputString.Split(sep1, InputString.Length);
foreach (string docNumb in colldocNumb)
{
//Varje split vid den förra delningen innehåller 2 informationsmängder. Country och Nummer info.
//splittar upp ytterligare, i två bitar.
string[] colldocNumbSep = docNumb.Split(sep2, 2);
//Skapar ett med byggstenar från split.
DocInfo objDocInfo = new DocInfo(colldocNumbSep[(int)Doc.CountryPart], colldocNumbSep[(int)Doc.NumberPart]);
//lägger till objekt som element i en Arraylist.
docnumber.Add(objDocInfo);
}
}
//Property för att returnera privat Arraylist.
public System.Collections.ArrayList DocNumberColl { get { return docnumber; } }
//Nästlad Klass:
//Klassen innehåller informationen från inputsträngen i ett uppdelat format, som senare kommer förenkla behandlingen av den.
public class DocInfo
{
private string country;
private string number;
public DocInfo()
{
}
public DocInfo(string Country, string Number)
{
//sätter privata attribut
this.country = Country;
this.number = Number;
}
//Property för respektive värdemängd.
public string Country { get { return country; } }
public string Number { get { return number; } }
}
}
}
Och användandet av ovan:
private void btnRun_Click(object sender, EventArgs e)
{
//Laddar DocNumber:
DocNumber objDocNR = new DocNumber(rtbInputfile.Text);
//Vill nu kunna göra något i stil med:
foreach (DocNumber.DocInfo docnumber in objDocNR.DocNumberColl)
{
rtbOutput.AppendText(docnumber.Country + "\t" + docnumber.Number + "\n");
}
//Faktum är... Att detta funkar utmärkt. Men tänker jag rätt rent arkitektur-mässigt -
//är detta en av nycklarna till nästlade klasser?
}
//Fredrik Leufkens
Sv: C# influerad arkitekturfråga
Några kommentarer:
Istället för
ArrayList docnumber = new ArrayList();
bör du använda en generic List klass:
List<DocInfo> docnumber = new List<DocInfo>();
Då blir den typesafe och du kan (ganska) enkelt lägga till sortering och jämförelse funktioner tex.
Sedan ser jag i det här fallet inte någon vits med att ha DocInfo klassen nästlad. Nästlade klasser har tex den egenskapen att de kan accessa privata fält i den klassen de är deklarerade i och det kan man ha nytta av för hjälpklasser som normalt endast används av klassen de är deklarerade i och så är inte fallet här (men det är kanske något jag missat och kanske någon annan egenskap av nästlade klasser du är ute efter).
Du har också en public get property till det privata ArrayList docnumber fältet. Detta innebär att alla som accessar docnumber via den publika propertyn har full access till ArrayListan och kan lägga till och ta bort element som de vill. Detta kan vara OK om man är medveten om det. I annat fall kan man returnera listans IEnumerator med GetEnumerator() utan att ha hela listan publik men ändå ha möjlighet att stega igenom den med foreach.
/RubenSv:C# influerad arkitekturfråga
Du frågar efter om jag söker en specifik egenskap inom den nästlade klassen. Detta vet jag inte med säkerhet - men för att förklara:
Så som jag tänker nu, är att jag vill att objektet av huvudklassen, i det här fallet DocNumber - skall (kunna) innehålla ett gäng objekt av en annan klass, I det här fallet DocInfo.
I förlängningen kanske det DocInfo dessutom innehåller objekt av en annan klass? osv osv.
Poängen är att: Jag vill kunna nå alla objekten utav respektive klass genom att skapa ett objekt av huvudklassen.
clsHuvudKlass objHuvudObjekt = new clsHuvudKlass(string InputString)
//Lek nu med tanken att konstruktorn för clsHuvudKlass behandlar InputStringen och
//skapar objekt av clsUnderKlass1, clsUnderklass1 skapar ytterligare objekt av clsUnderKlass2
//I clsUnderKlass2 så finns en lista med siffervärden (int) värden som skapades utifrån min InputString //i clsHuvudKlass som jag vill kontrollera ifall dem är likamed '10'
//Då gör jag så här:
foreach(clsHuvudKlass.clsUnderKlass1 objIterObjekt in objHuvudObjekt.Underklass1.UnderKlass2Collection)
{
//myNumber är en property i Underklass2
if(objIterObjekt.myNumber == 10)
objmyNumber += 5;
}
koden är skriven direkt här i och inte provad - ett och annat fel finns nog. Men principen är iallafall att kunna nå all information som skapades utifrån InputStringen inom respektive klass.
Är detta något som är görligt utan nästlade klasser?
Sv: C# influerad arkitekturfråga
Jämför tex med klassen string. Du kan ju använda ett string objekt som en member (eller fält om du vill) i din huvudklass utan att string är en nästlad klass till din huvudklass.
För att använda hjälpklasserna (eller underklasserna om du så vill) i andra klasser i andra projekt så använder du using <namespace> högst upp i filen som innehåller andra klasser som vill använda dina klasser. <namespace> ersätter du då med namespace där klasserna du vill komma åt finns.
En användning för att använda nästlade klasser i C# är när man vill ha hjälpklasser som bara används av huvudklassen och som då också kan komma åt protected eller private fält från huvudklassen. Dessa nästlade klasser skall då vara helt osynliga för andra klasser som använder huvudklassen. Så är ju inte ditt fall eftersom andra objekt skall kunna komma åt objekt som är av nästlade klasser.
Här är en länk: msdn2.microsoft.com/en-us/library/s9f3ty7f(VS.71).aspx
/RubenSv:C# influerad arkitekturfråga
//FredrikSv: C# influerad arkitekturfråga
Första klassen, DocNumber, innehåller en private List<DocInfo> docnumber och ett IEnumerable interface till listan.
Andra klassen är DocInfo som bara innehåller en sträng.
DocNumber constructorn lägger tre DocInfo objekt i docnumber.
public class DocNumber
{
private List<DocInfo> docnumber = new List<DocInfo>();
public DocNumber (string InputString)
{
DocInfo di1 = new DocInfo(InputString);
docnumber.Add(di1);
DocInfo di2 = new DocInfo("DummyString");
docnumber.Add(di2);
DocInfo di3 = new DocInfo("AnotherDummyString");
docnumber.Add(di3);
}
public IEnumerator<DocInfo> GetEnumerator ()
{
return (docnumber as IEnumerable<DocInfo>).GetEnumerator();
}
}
public class DocInfo
{
protected string _str;
public DocInfo (string InputString)
{
_str = InputString;
}
public string DiString
{
get
{
return _str;
}
}
}
Här är sedan exempel på koden som gör en foreach på DocNumber där DocInfo object från docnumber listan itereras.
DocNumber dn = new DocNumber("Test");
StringBuilder sb=new StringBuilder();
foreach(DocInfo di in dn){
sb.Append(di.DiString);
}
sb blir här "TestDummyStringAnotherDummyString" när koden körts.
Hoppas det hjälper.
/Ruben
Sv:C# influerad arkitekturfråga
Jag antar att i detta fallet så avses IENumerable som finns i Generic-namespacet och inte det ett steg utanför?
//FredrikSv: C# influerad arkitekturfråga
using System.Collections.Generic
using System.Collections.ObjectModel
Jag kom inte på det själv heller men "Google is, as always, your friend" :-)
/Ruben