Referencja do zmiennej globalnej [.net]

0

Witam wszystkich,
Tym razem chciałbym prosić o pomoc w przekazywaniu referencji w metodzie klasy.
Podam kod analogiczny, nie kopiowany aby było przejrzyściej:

//mam pliku "głównym" zmienną:
int Global = 0;//akurat zainicjalizowana w konstruktorze w oryginale

//chciałbym ją przekazać do metody klasy nie związanej z tą w głównym pliku, aby ta mi ją zmieniła, klasa jest w innym pliku
public ref class klasa {
// tutaj robię wskaźnik, który ma mi przy którejś z kolei instancji wskazywać na odpowiednią zmienną globalną
int^ refWklasie;

// chciałbym aby metda ustawiła mi referencję "refWklasie" na obiekt globalny
void ustaw(int^% referencjaPrzekazana) {\
     refWklasie = referencjaPrzekazana;
}

void zmień() {
     refWklasie = 1; // <b>Tutaj Bardzo Bym chciał Aby mi Zmieniło Zmienną Globalną "Global"</b>, ale nico się dzieje, za dużo chce?
}

};

Coś czuję, że robię to jak idiota, ale w CLI, to wiele rzeczy wygląda inaczej, jeśli chodzi o te referencje, które zastępują wskaźniki.(nie wiem czy dobrze, to nazywam referencją, gdzieś, to wyczytałem, chiałem zaznaczyć, że wiem, że nie jest to zwykły wskaźnik)

Pozdrawiam i dziękuję za każdą okazaną pomoc i radę,
Arrowman

0

Akurat dla inta, typu prostego, należałoby po prostu użyć wskaźnika:

public ref struct klasa {
	int* refWklasie;
	void ustaw(int* referencjaPrzekazana) {
		refWklasie = referencjaPrzekazana;
	}
	void zmień() {
		*refWklasie = 1;
	}
};

...

obiekt.ustaw(&Global);
obiekt.zmień();

a jeśli to nie ma być int... to powiedz co.

0

Przepraszam, że po takim czasie odpowiadam, ale szukałem rozwiązania na nowy problem:
Przy przekazywaniu parametrów do metody
obiekt.ustaw(&Global);
Wywala:
" cannot convert parameter 7 from 'cli::interior_ptr<Type>' to 'int *"
i nie znalazłem rozwiązania.
Ale już coś blisko:-) Nie używałem wcale *, bo mnie kiedyś jakiś błąd zmylił. To ile jest tych rodzajów wskaźników w CLI?

0

Miałbym prośbę, jeszcze się z tym męczę. Potrzebuję tego, a nie wiem już co robić. Zrobiłem taki mini-projekcik, żeby móc wygodnie testować rozwiązania. Mam w nim 2, które nie działają, ale przynajmniej się kompilują:-) Jako jedyne.
http://rapidshare.com/files/409885502/Referencje.rar.html
Mam nadzieję, że nie będzie nikomu serwer przeszkadzał, nie wiedziałem gdzie, to wrzucić, a nie mam hostingu.

Jeśli ktoś nie ma czasu ściągać, to wrzucę też kod:

form1.h

#pragma once


namespace Referencje {

	using namespace System;
	using namespace System::ComponentModel;
	using namespace System::Collections;
	using namespace System::Windows::Forms;
	using namespace System::Data;
	using namespace System::Drawing;
#include "myClass.h";

	/// <summary>
	/// Summary for Form1
	///
	/// WARNING: If you change the name of this class, you will need to change the
	///          'Resource File Name' property for the managed resource compiler tool
	///          associated with all .resx files this class depends on.  Otherwise,
	///          the designers will not be able to interact properly with localized
	///          resources associated with this form.
	/// </summary>
	public ref class Form1 : public System::Windows::Forms::Form
	{
	public:
		int Global;
		Referencje::myClass^ klasa;
			
		//
		Form1(void)
		{
			InitializeComponent();
			Global = 0;
			klasa = gcnew Referencje::myClass;
			//
			//TODO: Add the constructor code here
			//
		}

	protected:
		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		~Form1()
		{
			if (components)
			{
				delete components;
			}
		}
	private: System::Windows::Forms::Button^  button1;
	private: System::Windows::Forms::Label^  Lpokaz;

	protected: 

	private:
		/// <summary>
		/// Required designer variable.
		/// </summary>
		System::ComponentModel::Container ^components;

#pragma region Windows Form Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		void InitializeComponent(void)
		{
			this->button1 = (gcnew System::Windows::Forms::Button());
			this->Lpokaz = (gcnew System::Windows::Forms::Label());
			this->SuspendLayout();
			// 
			// button1
			// 
			this->button1->Location = System::Drawing::Point(12, 12);
			this->button1->Name = L"button1";
			this->button1->Size = System::Drawing::Size(75, 23);
			this->button1->TabIndex = 0;
			this->button1->Text = L"Zmień";
			this->button1->UseVisualStyleBackColor = true;
			this->button1->Click += gcnew System::EventHandler(this, &Form1::button1_Click);
			// 
			// Lpokaz
			// 
			this->Lpokaz->AutoSize = true;
			this->Lpokaz->Location = System::Drawing::Point(12, 76);
			this->Lpokaz->Name = L"Lpokaz";
			this->Lpokaz->Size = System::Drawing::Size(35, 13);
			this->Lpokaz->TabIndex = 1;
			this->Lpokaz->Text = L"label1";
			// 
			// Form1
			// 
			this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
			this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
			this->ClientSize = System::Drawing::Size(284, 264);
			this->Controls->Add(this->Lpokaz);
			this->Controls->Add(this->button1);
			this->Name = L"Form1";
			this->Text = L"Form1";
			this->Load += gcnew System::EventHandler(this, &Form1::Form1_Load);
			this->ResumeLayout(false);
			this->PerformLayout();

		}
#pragma endregion
	private: System::Void Form1_Load(System::Object^  sender, System::EventArgs^  e) {
				 Lpokaz->Text = "Global = " + Global.ToString();
			 }
	private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) {
				 klasa->ustaw(&Global);
				 klasa->zmien();
				 Lpokaz->Text = "Global = " + Global.ToString() + "    refWklasie = " + Convert::ToString(klasa->refWklasie);
				 // ^
				 klasa->ustaw2(&Global);
				 klasa->zmien2();
				 Lpokaz->Text += "\nGlobal = " + Global.ToString() + "    ref = " + Convert::ToString(klasa->ref);
				 //
			 }
	private: System::Void button2_Click(System::Object^  sender, System::EventArgs^  e) {
				 Lpokaz->Text = "Global = " + Global.ToString() + "    refWklasie = " + Convert::ToString(klasa->refWklasie);
			 }
};
}

myClass.h

#pragma once

using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Diagnostics;


namespace Referencje {

	/// <summary>
	/// Summary for myClass
	/// </summary>
	public ref class myClass :  public System::ComponentModel::Component
	{
	public:
		int* refWklasie;
		int zero;
		int^ ref;
		
		myClass(void)
		{
			
			InitializeComponent();
			zero = 0;
			refWklasie = new int(1);
			ref = gcnew int(0);
			*ref = zero;
			//
			//TODO: Add the constructor code here
			//
		}
		
		void ustaw(interior_ptr<int> referencjaPrzekazana){
			*refWklasie = *referencjaPrzekazana;
			//
		}
		
		void zmien() {
			*refWklasie = 3;
		}



		void ustaw2(interior_ptr<int> referencjaPrzekazana){
			*ref = *referencjaPrzekazana;
			//
		}
		
		void zmien2() {
			ref = 3;
		}


		myClass(System::ComponentModel::IContainer ^container)
		{
			/// <summary>
			/// Required for Windows.Forms Class Composition Designer support
			/// </summary>

			container->Add(this);
			InitializeComponent();
		}

	protected:
		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		~myClass()
		{
			if (components)
			{
				delete components;
			}
		}

	private:
		/// <summary>
		/// Required designer variable.
		/// </summary>
		System::ComponentModel::Container ^components;

#pragma region Windows Form Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		void InitializeComponent(void)
		{
			components = gcnew System::ComponentModel::Container();
		}
#pragma endregion
	};
}

Już nie mam pomysłów, kombinacje mi też się skończyły... Będę wdzięczny za pomoc i radę.

Pozdrawiam,
Arrowman

0

nie lepiej to zrobić zgodnie z regułami feng shui? opakować zmienną w jakąś klasę, np IntContainer, klasie dać getter/setter (albo nawet wystawić zmienną publiczną), i podawać innym obiektom tą samą instancję tego IntContainer'a? te inne mogą ją sobie zapisać, tym samym trzymając referencję do jednego i tego samego inta.
btw dział c++ jest w innym miejscu.

1

po pierwsze, coż to za diabeł:

form1.h
#pragma once

namespace Referencje {

    using namespace System;
    using namespace System::ComponentModel;
    using namespace System::Collections;
    using namespace System::Windows::Forms;
    using namespace System::Data;
    using namespace System::Drawing;

#include "myClass.h"; /// TUTAJ????

    /// <summary>

biorac pod uwage, ze plik "myclass.h" ma w srodku wlasna definicje namespace'a, przez taki zapis jak tutaj, powodujesz powstanie namespace'a Referencje::Referencje.. poza tym, co to za średnik za tym #include?

po drugie, zostaw interior_ptr<> na razie w spokoju. uzywajac typow ref uzywaj do nich notacji ^ oraz %

po trzecie i dalsze..

Coś czuję, że robię to jak idiota, ale w CLI, to wiele rzeczy wygląda inaczej, jeśli chodzi o te referencje, które zastępują wskaźniki.(nie wiem czy dobrze, to nazywam referencją, gdzieś, to wyczytałem, chiałem zaznaczyć, że wiem, że nie jest to zwykły wskaźnik)

Zadania wstepne dla Ciebie:

  1. zerknij sobie na doł strony http://msdn.microsoft.com/en-us/library/ms379617(VS.80).aspx na zawarta tam tabelke.
  2. http://msdn.microsoft.com/en-us/library/xey702bw.aspx
    3 i 4) http://msdn.microsoft.com/en-us/library/yk97tc08.aspx http://msdn.microsoft.com/en-us/library/8903062a.aspx

i jeżeli trzecie i czwarte pominiesz, mozesz sobie CLI darowac..

Nie jest to proste dla poczatkujacego i/lub nieznajacego .Net i/lub nieznajacego C++ i/lub nieznajacego WTHYATA*), dlatego jesli ktorychkolwiek z tych rzeczy nieznasz dobrze - zacznij od nauki C++ i C# osobno. c++ samwieszdlaczegochcesz, c# - zeby poznac .Net latwiej i szybciej. Dopiero potem wez sie za C++/CLI -- i byc moze bedziesz mial o wiele mniej pytan, a rozne tricki i magie i ograniczenia nagle okaza sie .... jedna z niewielu sensownych rzecza jaka autorzy mogli zrobic by umozliwic robienie "xyz" we miare ludzki sposob.


A teraz, zeby dac upust swojej grafomanii: wyciąg z tych linkow, po polsku, i w nomenklaturze 'zwyklego C++'.
w telegraficznym skrócie i nie opisując wszystkich mikrodetali ani nawet wszystkich mozliwych przypadkow

int ---- to typ normalny

int* ---- to wskaznik normalny, native, unmanaged
int& ---- to referencja normalna, native, unmanaged
int*& ---- to referencja normalna na normalny wskaznik, native, unmanaged

int^ ---- to wskaznik CLRowy, na cos pilnowanego przez GC, managed
int% ---- to referencja CLRowa, na jakies pole/property/lokalną klasy/metody ref, ktora zawiera int, managed
int^% ---- to referencja CLRowa, na jakies pole/property/lokalną klasy/metody ref, ktora zawiera wskaznik CLRowy, managed

itp. one się tak nie nazywają, ale tak je nalezy rozumiec.

POINTERY
Jest ogromna roznica pomiedzy zapisem int oraz int^, mimo ze kompilator w wielu miejscach pozwoli Ci to stosowac dowolnie i zamiennie. Pierwszy oznacza wiadomo-co. Drugi oznacza .Netowy odpowiednik int*. Stad:

  • prawidlowa wartoscia 'int' jest 5
  • prawidlowa wartoscia 'int^' jest gcnew int(5)
  • no i prawidlowa wartoscia 'int*' jest np. new int(5)
    Programisci dostali by jednak szału gdyby musieli ciagle pisac gcnew przed kazda wartoscia prostą piszac w CLI, wiec operacje (un)boxingu sa gdzieniegdzie robione automatycznie, po cichu:
int test1 = 5;    // normalne
int^ test2 = 5;     // cichy boxing
int^ test2 = gcnew(5);  // normalne
int inny = test1;  // normalne
int inny = test2;    // FAIL! unboxing nie jest cichy!
int inny = *test2;  // ok, normalne

REFERENCJE
int% odnosi sie do int, jest logicznym odpowiednikiem int&
int% odnosi sie do int, jest logicznym odpowiednikiem int*&

Mimo ze istnieje int% oraz int%, nie są to pełnoprawne referencje w sensie C++. Sa one dostępne/poprawne/możliwe tylko przy parametrach funkcji, nie mozesz stworzyc sobie pola klasy majacego typ int% albo int%. Ani ref-klasa, ani zwykla-klasa nie ma prawa zawierac pol o typie '%'. Obie natomiast moga zawierac zwykle referencje, int&.

Referencja typu '%' moze pojawic sie wiec tylko w parametrze metody, ^ moze pojawic sie gdziekolwiek. Co to oznacza?

  • To oznacza, ze jezeli przekazujesz cos przez INT, INT*, albo poprzez INT^ --- to przekazujesz (tak jakby) to przez "wartosc".
  • Jezeli zas przekazesz cos przez INT&, INT%, INT^% - przekazujesz to cos przez referencje.
  • W obiektach mozesz skladowac jedynie "INT", "INT^", "INT&"

To wszystko razem oznacza, ze mozesz normalnie skladowac w obiektach referencje do fragmentow innych niezarzadzanych obiektow.
To wszystko razem oznacza, ze nie mozesz normalnie skladowac w obiektach referencji do fragmentow innych zarzadzanych obiektow.

Do tych ostatnich, mozesz skladowac wskazniki.
Ale wskazniki te sa skladowane przez WARTOSC wskaznika (zmiana wskaznika int^ na 7 nie spowoduje ze to-na-co-kiedys-wskazywal nagle sie zmieni)

Twoj kod, przyciety i zmieniony, powinien Ci pokazac to co staram sie powiedziec.

#pragma once

namespace Referencje
{
	public ref class myClass
	{
	public:
		int^ mojaZmienna;
		//int% mojaReferencja;

		void ustaw(int^ pointerPrzekazany)
		{
			mojaZmienna = pointerPrzekazany;
		}

		void zmien()
		{
			mojaZmienna = gcnew int(333);
		}

		void ustaw_i_zmien(int^% referencjaPrzekazana)
		{
			mojaZmienna = referencjaPrzekazana;
			referencjaPrzekazana = gcnew int(444);
		}
	};
}

#pragma once

#include "myClass.h"

namespace Referencje
{
	using namespace System;
	using namespace System::ComponentModel;
	using namespace System::Windows::Forms;

	public ref class Form1 : public Form
	{
	public:
		int^ zmiennaFormatki;
		myClass^ innyObiekt;

		//int zmiennaFormatki2;   <----[UWAGA]
		//myClass innyObiekt2;   <----[UWAGA]

		Form1(void)
		{
			InitializeComponent();
			zmiennaFormatki = gcnew int(111);
			innyObiekt = gcnew Referencje::myClass();
		}

	protected:
		~Form1()
		{
			if (components)
			{
				delete components;
			}
		}
	
	private: System::Windows::Forms::Button^ button1;
	private: System::Windows::Forms::Label^ Lpokaz;

	private:
		/// <summary>
		/// Required designer variable.
		/// </summary>
		System::ComponentModel::Container ^components;

	#pragma region Windows Form Designer generated code
		TUTAJ WSZYSTKO TAK SAMO JAK W TWOIM KODZIE. wyciete dla oszczednosci miejsca
	#pragma endregion

	private: System::Void Form1_Load(System::Object^  sender, System::EventArgs^  e)
		{
			Lpokaz->Text = "zmiennaFormatki = " + zmiennaFormatki->ToString();
		}

	private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e)
		{
			Lpokaz->Text = "zmiennaFormatki = " + zmiennaFormatki->ToString() + "\r\n";

			Lpokaz->Text += "\r\nOdpalam innyobiekt->Ustaw(int^) z arg (this->zmiennaFormatki)\r\n";
			innyObiekt->ustaw(zmiennaFormatki);
			Lpokaz->Text += "zmiennaFormatki = " + zmiennaFormatki->ToString() + " i przez pointer w innym obiekcie = " + Convert::ToString(innyObiekt->mojaZmienna) + "\r\n";

			Lpokaz->Text += "\r\nOdpalam innyobiekt->Zmien() z arg (this->zmiennaFormatki)\r\n";
			innyObiekt->zmien();
			Lpokaz->Text += "zmiennaFormatki = " + zmiennaFormatki->ToString() + " i przez pointer w innym obiekcie = " + Convert::ToString(innyObiekt->mojaZmienna) + "\r\n";

			Lpokaz->Text += "\r\nOdpalam innyobiekt->ustaw_i_zmien(int^%) z arg (this->zmiennaFormatki)\r\n";
			innyObiekt->ustaw_i_zmien(zmiennaFormatki);
			Lpokaz->Text += "zmiennaFormatki = " + zmiennaFormatki->ToString() + " i przez pointer w innym obiekcie = " + Convert::ToString(innyObiekt->mojaZmienna) + "\r\n";
		}
	};
}

Roznica pomiedzy trzymaniem w Form1 pola typu "int zmienna" a pola typu "int^ zmienna" jest dokladnie taka sama jak w zwyklym C++ miedzy "int" a "int*".

Nie powinienem w tym przykladzie uzywac na sile int^ dla tego pola, gdyz spokojnie wystarczyloby int. Skonstruowalem jednak przyklad w ten sposob, zebys mogl zobaczyc jak wyglada 'kanoniczny' przyklad obiektów .NET, "takie jak w C#".

Spojrz teraz na linie oznaczone "[UWAGA]"

		//int zmiennaFormatki2;   <----[UWAGA]
		//myClass innyObiekt2;   <----[UWAGA]

Jesli je odkomentujesz, kod bedzie nadal poprawny. Jesli poprzednie zmienne wymienisz na te dwie, to poza zmiana kropek na strzalki przy wywolaniach metod, kod bedzie poprawny. Nie beda to jednak typowe obiekty .NET, nie beda "takie jak w C#", poniewaz...... w tym przypadku, jeżeli zniszczysz obiekt FORM1, one tez zostana "zniszczone" "natychmiast". W 'kanonicznym' przykladzie nie bylo by w ten sposob. Tam, przy niszczeniu FORM1, jego skladowe bedace wszystkie poitnerami^, zostaly by od niego odczepione i stanely by gdzies-za-nim w kolejce do GC, i chwile potem pewnie tez by zostaly 'zniszczone', ale nie natychmiast.

"Niszczenie" i "Natychmiast" sa w cudzyslowach, gdyz sposob niszczenia zalezy od ... typu danego pola Twojej ref-klasy.
Jesli niszczony-natychmiast jest to typ prosty jak int, wlasciwie nie dzieje sie nic. Jego miejsce w pamieci zas wyparuje z wlascicielem (jak GC sprzatnie wlasciciela).
Jesli niszczony-natychmiast jest to typ natywny jak Twoj wlasny " struct Point{ int x,y; };", to wolany jest jego destruktor. Jego miejsce w pamieci zas wyparuje z wlascicielem (jak GC sprzatnie wlasciciela).
Jesli niszczony-natychmiast jest to typ ref jak Twoj wlasny " ref class MyClass{ ... };", to wolany jest jego "destruktor", czyli metoda Dispose, o ile taka w nim istnieje. Jesli nie - nie dzieje sie nic. Tak czy owak, obiekt nie jest niszczony, tylko "trafia do kolejki GC" i jego miejsce zostanie sprzatniete ..kiedystam.

Koniec telegraficznego skrotu.
Mam nadzieje ze rozumiesz i "czujesz" roznice miedzy ref i 'nie-ref', miedzy w polami X oraz X i w jaki sposob "dla Twojej wygody" kompilator Cie "oszukuje" pozwalajac zapisac int blah = 5

*) WTHYATA = What The Hell You Are Talking About?!

PS. Ad. niemożnośc skladowania int% w klasie powoduje, że jeśli bardzo sie przy tym upierasz, pozostaje rozwiązanie podane przez ŁF. Lub trzymanie ref do calego obiektu majacego tego int'a i niech ten obiekt wystawia setter'a, lub zachowanie sobie delegate ktory wywola setter/ustawi zmienna, lub ..... , lub inne "reczne" sposoby

0

Mimo, że mi bardzo pomogłeś, to mam jeszcze jeden problem. Chciałbym mieć w klasie pointer, który zapamiętywałby mi adres spoza klasy i pliku. Nauczyłeś mnie już zmieniać taką zmienną "zewnętrzną", tylko wciąż mam problem z zapamiętaniem na "dłużej" tego adresu zmiennej. Nie mogę w klasie tworzyć zmiennych typu ^% i w tym jest "problem".
Dokładniej, to mam klasę w której jest button i zdarzenie on_click. Do tego zdarzenia nie mogę podawać jakich chce argumentów, dlatego w klasie, której częścią jest ten button, chciałbym w każdej instancji przechowywać adres pewnej zmiennej "zewnętrznej", aby on_click to zmieniał. W każdej instancji button ma takie samo on_click, dlatego chcę się odwoływać do innych zmiennych instancji klasy, aby rozróżniać poszczególne buttony.
Dziękuję jeszcze raz za wszystko.

0

Nie całkiem rozumiem, co dokladnie chcesz osiągnąć taką konstrukcją - czy chodzi Ci o utworzenie np. 100 obiektów danej klasy, każdy z buttonem i onclickiem wg. jej definicji, i ta setka buttonow ma sie odnosic do czegos-pojedynczego i wspolnie to cos modyfikowac?

Ogólnie, dobry 'problem' postrzegasz, rozwiązałbyś to w 'tradycyjny, c++owy sposob' poprzez stworzenie w tejze klasie-z-buttonem (niech sie ona zwie klasą Olowek) jakiegos pola & i ustawianie go podczas konstrukcji obiektu. Tutaj się "nie da", gdyz nie mozliwe jest pole typu blah%, mozliwe sa tylko pola blah.

Ale, ale, jesli by to bylo mozliwe. Albo bedac w 'zwyklym' C++, w jaki sposob by to, w ogóle, 'normalnie', miało działać?

Mialbys -cos-, (nazwijmy to klasa Książka, obiekt pantadeusz), to coś zawierałoby jakas zmienna (typ string, nazwa "pierwsza_linijka_pierwszej_strony"). Twoje sto wystapien ołówków teraz musialoby przechowywac -odniesienie- to tegoz ..czego? do tego stringa? stringi sa nie-zmienialne w .Net (no, prawie niezmienialne, ale sza). Pamietanie odniesienia do tego stringa da mozliwosc czytania, ale nie zapisu. W takim razie mozna zapamietac wskaznik-na-niego lub referencje-na-niego, jest to dokladnie to samo, tylko w zapisie inne. Tworzysz wiec obiekt Ksiazka, umieszczasz w zmiennej 'pantadeusz', z tej zmiennej czytasz adres pola 'pierwsza_linijka_pierwszej_strony' i wszystkim olowkom ten adres podajesz. Uaaa teraz moga zapisywac wspolnie!

Ale.. co to robi z "hermetycznoscia"/"hermetyzacja"?
Ktos, olowek, wlasnie poznal -wewnetrzna- konstrukcje obiektow typu Książka? Nie. Nie poznal. Olowek tylko zna "adres-na-string-do-zapisu". Nie ma zielonego pojecia o istnieniu Ksiazki. Tutaj jest w miare ok. Natomiast zupelnie inny kod, ten ktory uzywa ksiazki i buduje olowki, musi nagle obiekt książki "wybebeszac", aż do żywej formy binarnej (jak inaczej pobrac adres?) i stamtad propagowac do olowkow wskazniki, ktore nikt nie wie skad pochodza, jaki maja czas zycia, itp. To bedzie dzialac, ale nie wyglada dobrze i prosi sie o bledy.

W .Net podobnie jak i w Javie, uzywanie "zywych" wskaznikow jest "be". To na pewno juz wiesz, stad sie bierze blah zamiast blah* -- obiekty sa śledzone przez GC, czasem sa przesuwane w pamieci, zeby uniknac jej fragmentacji itp. Co to jest wziecie referencji do pola obiektu? & to zapis uproszczony na ot, kolejny . Referencja do Klasa::podpole to nic innego jak Klasa::podpole, tylko przetlumaczona w jezyku na zapis 'kropkowy' zamiast 'strzalkowy'. Jak wziac referencje do KAWAŁKA obiektu blah ktory moze zostac przez GC przesuniety w inne miejsce pamieci? Niby mozna, ostatecznie skoro blah^ oznacza referencje do obiektu ktory moze byc przesuwany przez GC, to i na pewno dalo byc sie wziac referencje do ten-obiekt-plus-szesc-oczek. Ale nie przewidziano tego **)

Aby uproscic GC, by ten byl jak najszybszy i mial jak najmniej potencjalnych bledow, zminimalizowano rzeczy ktorych GC musi pilnowac. GC pilnuje tylko calych obiektow. Jesli chcesz do czegokolwiek wziac referencje, "odniesienie", to to cos MUSI BYC CALYM OBIEKTEM pilnowanym przez GC.

To daje Ci dwie mozliwosci rozwiazania problemu.

A) Niech klasa Olowek zamiast znac string* do zapisu - niech zna klasę Książka, niech klasa Książka wystawia publiczne cos (metode, wlasnosc, pole, cokolwiek, moze nawet byc owo pole 'pierwsza_linijka_pierwszej_strony' jako publiczne). Od tej pory konstruktor Olowka pobiera Ksiazka^, natomiast gdzies w metodach olowka napiszesz zmienna->pierwsza_linijka_pierwszej_strony = "blah" zamiast *zmienna = "blah".

To jest banalne, zadziala na pewno, ale uszkadza hermetyzacje..

B) Zastosowac to, co Ci Od razu ktos-tam odpisal na forum: OPAKOWAC to pole w mikro obiekcik. Niech stanie sie klasa "WTF" ktora bedzie miala jedno publiczne pole "string napis". Niech Ksiazka od tej pory zamiast "string pierwsza_linijka_pierwszej_strony" ma "WTF pierwsza_linijka_pierwszej_strony". Niech Olowek zamiast brac string* od teraz pobiera "WTF". Obiekty Ksiazka podczas swojej konstrukcji niech robia "pierwsza_linijka_pierwszej_strony=gcnew WTF();". Od teraz olowki konstruujesz podajac im wartosc zmiennej "pierwsza_linijka_pierwszej_strony". Od teraz ksiazka uzywa swojej pierwszej linijki tak: "pierwsza_linijka_pierwszej_strony->napis = "bum"".

Od teraz olowek pamieta odniesienie pojedynczego, calego, obiektu typu WTF zawierajacego jeden string. I przypadkiem ten sam obiekt jest tez uzywany w klasie Ksiazka. Ksiazka wie o calej zawartosci WTF, nie wie o tym ze jest uzywana przez Olowek. Zadna jej czesc nie jest 'wystawiona' publicznie. Wystawiony publicznie jest jakis obiekt WTF ktorego ona uzywa, a nie jej binarne wnetrze. Olowek nie wie o ksiazce, wie ze moze zmieniac zawartosc calego WTF. Nigdzie nie ma wskaznikow ani referencji-do-pol-obiektow. Sa tylko odniesienia do calych obiektow, takie, ktore GC moze i umie sledzic. Wszyscy sa happy, mamy Arkadie, Niebo i Pola Elizejskie w jednym. Osobą nieszczesliwa zostajesz Ty, gdyz musisz napisac 2-3 razy wiecej kodu, zeby wszystkich innych zadowolic na byle 'referencje do stringa'.

O ile prosciej by bylo, gdyby string byl ZMIENIALNY? Przeciez string to tez caly obiekt, sledzony przez GC. String^ wtf = pantadeusz.pierwsza_linijka_pierwszej_strony calkowicie by wystarczyl, Olowki moglyby sobie pamietac te jedna instancje stringa, klasa Ksiazka tez by sobie ja pamietala. Wszyscy uzywali by jednego stringa i go czytali/zmieniali. Mniej kodu, krocej i zwieźlej.
Ale, stringi sa niezmienalne (niby) i kropka. Z "powodow wydajnosciowych i bezpieczenstwa" stwierdzono tak jak w Javie, ze kazda zmiana stringa tworzy nowy string a stary zostaje niezmieniony. Bo podobno ze stringow wiecej sie czyta niz do nich pisze, i podobno tak jest lepiej. Coż.

I z tego powodu, tak jak i w Javie, stringi sa ZMIENIALNE. Tylko Ty tego nigdy NIE ZOBACZYSZ w ten sposob. Dla programisty, string jest nietykalny i tylko do odczytu. Natomiaist jest on normalna klasa, jego bebechy calkowicie normalne i nawet on sam zawiera (niepuliczne) metody pozwalajace go zmieniac. Z tych metod korzysta (podobnie jak w Javie) klasa StringBuilder.

Niech klasa Ksiazka ma pole pierwsza_linijka_pierwszej_strony typu StringBuilder lub StringBuilder. Niech olowek pobiera StringBuidler. Niech wszyscy uzywaja tej klasy do operacji na stringu - sa metody Clear, Append, Replace itp. Jak jest komus potrzebny caly, "poskladany" string - niech sobie wywola stringbuilder->ToString() i dostanie wynikowego stringa *).

I voil'a. Mamy rozwiazanie typu A+B.
Napisalismy minimum kodu, jest pojedyncza zmienna, do niej bierzemy odniesienie normalne ^-kowe.. Zadnych WrapperowNaString z palca, zadnych & czy %.
Ale uzywamy wrappera na string, nazywajacego sie StringBuilder, uzywamy calego obiektu, sledzonego w calosci przez GC.
(defacto, jest to rozwiazanie czysto-B, ale nie Ty piszesz wrapper...)

I tak to powinno wygladac.
Jednak, w momencie gdy eksponujesz NIE-STRING, to oczywiscie nie bedzie juz w systemie klasy "DateBuilder" czy "IntegerBuilder".. Zostana wtedy rozwiazania czyste-A, albo czyste-B.
Ale-Ale, jesli to nie bedzie int, nie string, nie date, nie-cokolwiek-valuetype, tylko jesli bedzie to jakas Twoja KLASA, np. pole bedzie typu "MojaKlasaZPieciomaZmiennymi" --- to juz jestes w domu! Skoro to pole eksponuje OBIEKT, to calosc jego juz jest sledzona i smialo kopiujesz sobie owo MojaKlasaZPieciomaZmiennymi dokadkolwiek "i juz"..

Jedyne problemy beda sprawias STRUKTURY, value-type, int, date, i Twoje struktury. Sledzonej referencji do nich nie wezmiesz, dostaniesz KOPIE, a nie oryginal, trzeba bedzie je opakowywac.. String nie jest value type. String jest klasa, zaprojektowana aby jej obiekty wygladaly jak niezmienialne, wiec dostarczono StringBuilder. Wszystkie int, date itp sa valuetype, sa strukturami, dla nich nie ma builderow.

Tym razem, ponizsze przypisy przeczytaj koniecznie:

*) uwaga. jesli od stringbuildera pobierzesz string, a potem stringbuilderowi kazesz cos zmienic --- MOZE sie okazac, ze ten "niezmienialny" string ktory dostales tez sie zmieni. Niepamietam, ale wydaje mi sie ze stringbuiler dziala na jednym i tym samym obiekcie stringa caly czas. To oznacza, ze po "zakonczonej" budowie stringa, musisz z-re-konstruowac sobie nowy StringBuilder. Sprawdz to koniecznie, np:
Stringbuilder blah;
blah.append "mama";
string x = blah.tostring()
blah.clear
ile teraz wynosi X?
jesli sie wyczysci, to znaczy, ze jednak "StringBuilder" nie pomaga w 100% i ze jesli StringBuildery zyją w Olowkach bardzo dlugo, to musisz bardzo uwazac z tym, co dostajesz z jego ToString i komu tego stringa podajesz dalej.. Albo samemu dbac o to, zeby powstala KOPIA tego stringa co builder zwraca, zeby ja oderwac od nastepnych zmian..

) W C#, w CLI, w .Net istnieje cos takiego jak "pinning", "pin"-nięcie, "przypięcie" obiektu, przybicie gwoździem do miejsca w pamieci. Obiekt z-pin-owany NIE MOZE byc przesuniety przez GC i zawsze (az do un-pin) pozostaje w miejscu. Od tej pory mozna do niego brac ZWYKLE unmanaged wskazniki, Blah = &obiekt, mozna brac & do jego wnetrza, mozna robic wszystko co na zwyklym natywnym obiekcie C++. Minus - zwykle pinning jest scopeowy, abys nigdy nie zapomnial zdjąć gwoździa i nie zostawiał przybitych obiektow. Jesli nie jest (a tu bys potrzebowal..), wprowadza duzo zamieszania w cyklach zycia obiektow i musisz sie dodatkowo narobic aby samemu zadbac o to aby w koncu gwozdzia wyciagnac w dobrym momencie. Zeby nie bylo -- jest jeszcze marshalling i pare innych rzeczy ktore mimo ze siedzisz w .Net i masz GC - pozwalaja na dzialanie na nizszym poziomie i na zabawie z GC w kotka i myszke, ale wtedy z kolei czesciej operujesz na .. IntPtr niz na typ czy typ^, a to juz tak jakbys caly czas pisal uzywajac void*.. czyli lekko kijowo i malo bezpiecznie.

Serio. Rozwiazanie klasy B) albo czysty C++ bez .Net. Ale lepiej zaczac przyzwyczajac do podejscia B), gdyz jesli chcesz pisac 'bezpiecznie' w czystym C++, to tez rozwiazanie B sie czesto stosuje, hermetyzacja jest niestety czesto warta tych paru/nastu linii kodu wiecej

PS.
No tak, zapomnialem - jesli mowa aktualnym .Net :), to sa jeszcze delegaty i funkcje anonimowe. Co to oznacza? Ze Ksiazka ma prywatne pole "linijka", ze Olowek pobiera FUNKTOR/DELEGATA ZMIANY napisu, ze Ksiazka dostarcza FUNKTOR/DELEGATA ZMIANY, ze tworca setki olowkow bierze od panatadeusza ow funktor i podaje go kazdemu olowkowi. Olowki chcac zmienic tresc, biora owa tresc i podaja funktorowi. Czyli wywoluja funktor jak metode i podaja mu tresc w parametrze.. W C++ tez sie to stosuje, jest bardzo, bardzo wygodniejsze niz wrappery.. ale i miejscami bardziej pokrecone i bez odpowiedniej wprawy --- mniej czytelne.

0

Wow! Dziękuję za wszystko. Teraz nie rozumiem jak mogłem mieć z tym problem:-) Twój post, to istne objawienie. Teraz sam nie wiem po co mam tworzyć sobie samotny obiekt typu int, jak rzeczywiście mogę go obudować. W sumie, to próbowałem coś podobnego z dziedziczeniem, ale coś mi nie wyszło(już we wcześniejszym Twoim poście pokazałeś na powód), ale teraz by wyszło, a zresztą mogę sobie zrobić obiekt static. To tak jakbym przedtem spoglądał przez mgłę, a teraz mgła opadła i widzę kilka razy dalej!
Dziękuję za wszystko. Nie pomyślałbym, że ktoś może poświęcić aż tyle czasu na pomoc. I, to takiemu noobowi, co pewnie jest nudne. No i jeszcze nie widząc całego projektu, tyle się domyślić. Niesamowite! Dzięki!

1 użytkowników online, w tym zalogowanych: 0, gości: 1