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

How to check in AngularJS if a service was register or not

There are cases when you need to check in a service or a controller was register in AngularJS.
For example a valid use case is when you have the same implementation running on multiple application. In this case, you may want to intercept the HTTP provider and add a custom step there. This step don’t needs to run on all the application, only in the one where the service exist and register.
A solution for this case would be to have a flag in the configuration that specify this. In the core you would have an IF that would check the value of this flag.
Another solution is to check if a specific service was register in AngularJS or not. If the service was register that you would execute your own logic.
To check if a service was register or not in AngularJS container you need to call the ‘has’ method of ‘inhector’. It will return TRUE if the service was register.
if ($injector.has('httpInterceptorService')) { $httpProvider.interceptors.push('httpInterceptorService&#…

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…