Skip to main content

Structure and "Cannot modify the return value of XXX because it is not a variable" error

In ziua de azi aproape toata lumea foloseste clase. Cand aude cineva de struct, se uita la tine ciudat, de parca ai venit din evul mediu. In unele cazuri o structura este o optiune mult mai buna decat o clasa, dar nu despre asta o sa discutam astazi. O sa discutam putin despre o eroare pe care multa lume o primeste cand lucreaza cu struct.
Cannot modify the return value of XXX because it is not a variable
Cod sursa:
struct Foo
{
public int Value { get; set; }
}

class FooContainer
{
public FooContainer()
{
Foo = new Foo() {Value = 1};
}
public Foo Foo { get; private set; }
}
...
FooContainer fooContainer=new FooContainer();
fooContainer.Foo.Value = 2; // !!!
Cauza la aceasta eroare nu este limbajul, Visual Studio sau orice alt dezvoltator care a lucrat la proiect. Este vina celui care vrea schimbe valoarea unei date dintr-o structura.
O structura poate sa contina constante, field-uri, propietati, operatori, evenimente, index-uri etc. Aceasta se poate folosii 'oarecum' ca si o clasa, dar este un VALUE TYPE pana la moarte.
Ce inseamna asta? Nu se poate sa mosteneasca o alta structura, nu poate sa fie derivata, nu poate sa contina valoarea NULL etc. Pentru o definitie completa va rog sa va uitati la urmatoarea adresa http://msdn.microsoft.com/en-us/library/s1ax56ch%28v=vs.71%29.aspx
Ce ne intereseaza pe noi cel mai mult este ca structurile sunt "IMUTABILE" din anumite puncte de vedere - odata ce au fost create, starea lor interna nu poate sa fie schimbata. Nici o valoare din interiorul unei structuri nu poate sa fie modificata. De fapt, ce se intampla in spate cand facem o noua "referinta" la structura, este sa se creeze o noua copie a acesteia. Cand referim o alta structura se copiaza valoarea s-a si nu referinta.
Din aceasta cauza orice modificare pe care noi o facem la structura, se va face pe copia ei si nu pe structura insasi. Daca dorim sa facem acest lucru atunci este nevoie sa inlocuim toata structura, creand una noua.
Din aceasta cauza un field se poate initializa o singura data, in momentul in care construim structura. Orice modificare a unui field dupa acest moment nu o sa mai poata sa fie facuta.
Nu trebuie sa uitam ca nu are rost sa ne definim un contructur pentru o structura - care sa itializeze campurile cu valoare defapt, deoarece by default o sa avem mereu unul care va face acest lucru(pentru fiecare camp in parte).
Enjoy!

Comments

  1. //...
    FooContainer fooContainer = new FooContainer();
    Foo foo = fooContainer.Foo;
    foo.Value = 2;
    /// atentie doar ce faci cu foo mai departe, deoarece fooContainer.Foo (originalul) nu va reflecta modificarea ta - Foo fiind un value type
    /// restrictia de a modifica un value type returnat de o proprietate sau metoda vine tocmai din faptul ca iti returneaza o copie de care trebuie sa ai grija explicit atunci cand o modifici.
    /// Imutabilitatea nu are legatura cu asta iar value type-urile nu sunt imutabile prin definitie. exemplul de mai sus o demonstreaza.

    BTW, foarte fain topicul!

    ReplyDelete
  2. Din cauza asta am pus cu ghilimele imutabil. In exemplul dat mai sus se comporta din unele puncte de vedere ca un obiect imutabil.

    ReplyDelete
  3. Cauza e si limbajul oarecum, fiindca struct in C# e alt animal decat in C++ (unde e prea putin diferit fata de un class )- cine a invatat mai intai C++ nu se asteapta la multe din "surprizele" din C# (care au logica lor, da' ...) - probabil mai tii minte discutia: http://ronua.ro/CS/forums/t/8066.aspx :)

    ReplyDelete

Post a Comment

Popular posts from this blog

ADO.NET provider with invariant name 'System.Data.SqlClient' could not be loaded

Today blog post will be started with the following error when running DB tests on the CI machine:
threw exception: System.InvalidOperationException: The Entity Framework provider type 'System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer' registered in the application config file for the ADO.NET provider with invariant name 'System.Data.SqlClient' could not be loaded. Make sure that the assembly-qualified name is used and that the assembly is available to the running application. See http://go.microsoft.com/fwlink/?LinkId=260882 for more information. at System.Data.Entity.Infrastructure.DependencyResolution.ProviderServicesFactory.GetInstance(String providerTypeName, String providerInvariantName) This error happened only on the Continuous Integration machine. On the devs machines, everything has fine. The classic problem – on my machine it’s working. The CI has the following configuration:

TeamCity.NET 4.51EF 6.0.2VS2013
It seems that there …

Entity Framework (EF) TransactionScope vs Database.BeginTransaction

In today blog post we will talk a little about a new feature that is available on EF6+ related to Transactions.
Until now, when we had to use transaction we used ‘TransactionScope’. It works great and I would say that is something that is now in our blood.
using (var scope = new TransactionScope(TransactionScopeOption.Required)) { using (SqlConnection conn = new SqlConnection("...")) { conn.Open(); SqlCommand sqlCommand = new SqlCommand(); sqlCommand.Connection = conn; sqlCommand.CommandText = ... sqlCommand.ExecuteNonQuery(); ... } scope.Complete(); } Starting with EF6.0 we have a new way to work with transactions. The new approach is based on Database.BeginTransaction(), Database.Rollback(), Database.Commit(). Yes, no more TransactionScope.
In the followi…

GET call of REST API that contains '/'-slash character in the value of a parameter

Let’s assume that we have the following scenario: I have a public HTTP endpoint and I need to post some content using GET command. One of the parameters contains special characters like “\” and “/”. If the endpoint is an ApiController than you may have problems if you encode the parameter using the http encoder.
using (var httpClient = new HttpClient()) { httpClient.BaseAddress = baseUrl; Task<HttpResponseMessage> response = httpClient.GetAsync(string.Format("api/foo/{0}", "qwert/qwerqwer"))); response.Wait(); response.Result.EnsureSuccessStatusCode(); } One possible solution would be to encode the query parameter using UrlTokenEncode method of HttpServerUtility class and GetBytes method ofUTF8. In this way you would get the array of bytes of the parameter and encode them as a url token.
The following code show to you how you could write the encode and decode methods.
publ…