2

this is an example from Unity3D :

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ParticleLauncher : MonoBehaviour {

    public ParticleSystem particleLauncher;
    public ParticleSystem splatterParticles;
    public Gradient particleColorGradient;
    public ParticleDecalPool splatDecalPool;

    List<ParticleCollisionEvent> collisionEvents;

    void Start () 
    {
        collisionEvents = new List<ParticleCollisionEvent> ();
    }

    void OnParticleCollision(GameObject other)
    {
        ParticlePhysicsExtensions.GetCollisionEvents (particleLauncher, other, collisionEvents);

        for (int i = 0; i < collisionEvents.Count; i++) 
        {
            splatDecalPool.ParticleHit (collisionEvents [i], particleColorGradient);
            EmitAtLocation (collisionEvents[i]);
        }

    }

    void EmitAtLocation(ParticleCollisionEvent particleCollisionEvent)
    {
        splatterParticles.transform.position = particleCollisionEvent.intersection;
        splatterParticles.transform.rotation = Quaternion.LookRotation (particleCollisionEvent.normal);
        ParticleSystem.MainModule psMain = splatterParticles.main;
        psMain.startColor = particleColorGradient.Evaluate (Random.Range (0f, 1f));

        splatterParticles.Emit (1);
    }

    void Update () 
    {
        if (Input.GetButton ("Fire1")) 
        {
            ParticleSystem.MainModule psMain = particleLauncher.main;
            psMain.startColor = particleColorGradient.Evaluate (Random.Range (0f, 1f));
            particleLauncher.Emit (1);
        }

    }
}

you can find the rest of the code in here :

enter link description here

My question is about EmitAtLocation() function , Inside this function we have

ParticleSystem.MainModule psMain = splatterParticles.main; 

MainModule is a struct and I know that the struct in C# passed by value not by reference , but here we have psMain variable , if we do any change to that variable the splatterParticles.main will be effected , its looks like the struct has been passed by reference , is that Possiable in C# or am I missing something ?

Honey
  • 169
  • 1
  • 8
  • Everything in c# is passed by reference, except when passed as `ref` or `out`. for a more detailed information, [read this.](https://stackoverflow.com/questions/38344333/difference-between-passing-a-reference-type-and-a-value-type-as-argument-to-a-me/38346592#38346592) – Zohar Peled Dec 17 '17 at 12:56
  • 6
    @ZoharPeled, you mean by value – NotADeveloper Dec 17 '17 at 13:07
  • 1
    Reference types contained within structs are still reference types so changes to their properties will reflect in all references to the object. – Crowcoder Dec 17 '17 at 13:27
  • @Crowcoder thats true but we assign a struct to a struct so even if there is a reference type variable they will be replaced when the whole struct body is replaced – Honey Dec 17 '17 at 13:31
  • @NotADeveloper Correct, sorry, stupid mistake – Zohar Peled Dec 17 '17 at 13:31
  • 1
    @ZoharPeled if you see my example you will see that , we assign struct variable to another variable and it act as reference type , but in your example you only concern about passing parameter to a function by reference , will that is not what this question about – Honey Dec 17 '17 at 13:36
  • Your example is missing the most relevant part of the question - `MainModule` struct and `startColor` member (property?) – Ivan Stoev Dec 17 '17 at 13:42
  • @Honey I think you have lost me a bit about what gets assigned to what and then dereferenced and assigned.... But there is no bug in c# regarding passing by value, what you are seeing must be because your structs contain reference types. – Crowcoder Dec 17 '17 at 13:43
  • @Ivan Stoev MainModule is written by unity so I cant access them , I just want to know how they do it ? – Honey Dec 17 '17 at 13:48
  • @Crowcoder if thats the case can give me an example for a similar situation in C# , what I change in the psMain is a value types like an float psMain.startSize not refrence type . – Honey Dec 17 '17 at 13:49
  • @Honey I would instead ask you to post a complete, minimal and verifiable example that demonstrates the behavior. – Crowcoder Dec 17 '17 at 13:52
  • @Crowcoder I will post it but its based on Unity , is that ok ? – Honey Dec 17 '17 at 13:54
  • MainModule is a struct. ParticleSystem is a class that inherits from Component. This cannot be possible. `MyClass c = new MyClass(); MyStruct s = c.Struct; s.Value = 15; Console.WriteLine(c.Struct.Value);`... assuming an "uninitialized" `Value` this MUST print `0`. There must be something else undergoing that changes the original struct. – Tommaso Belluzzo Dec 17 '17 at 13:58
  • @Honey well, I'm dubious that would be "minimal" and "verifiable" . Something we can paste into a console app is what I'm thinking. The issue should not be Unity specific if you are talking about value vs reference semantics. – Crowcoder Dec 17 '17 at 13:58
  • @Crowcoder this something I only see with unity api , I dont know how its been done , otherwise I didn't need to ask the question – Honey Dec 17 '17 at 14:02
  • @Tommaso Belluzzo that's what I'm thinking ,thanks for pointing out . – Honey Dec 17 '17 at 14:04
  • I think @Kevin nailed it. – Crowcoder Dec 17 '17 at 14:04
  • 1
    `MainModule is written by unity so I cant access them` Decompiling .NET assemblies is trivial. Just download one of the decompilers available (JustDecompile, DotPeek, IlSpy, ...) and open the `UnityEngine.dll` file to see how it works – Kevin Gosse Dec 17 '17 at 14:06

2 Answers2

4

There's a trick behind the scenes.

First, let's have a look at the ParticleSystem.main property:

public ParticleSystem.MainModule main
{
  get
  {
    return new ParticleSystem.MainModule(this);
  }
}

We see that it returns a new MainModule struct, that takes this as parameter. Then let's have a look at the MainModule constructor:

  private ParticleSystem m_ParticleSystem;

  internal MainModule(ParticleSystem particleSystem)
  {
    this.m_ParticleSystem = particleSystem;
  }

So we're keeping a reference to ParticleSystem around (note that ParticleSystem is a class). Finally, the startColor property:

  public ParticleSystem.MinMaxGradient startColor
  {
    set
    {
      ParticleSystem.MainModule.SetStartColor(this.m_ParticleSystem, ref value);
    }
    get
    {
      ParticleSystem.MinMaxGradient gradient = new ParticleSystem.MinMaxGradient();
      ParticleSystem.MainModule.GetStartColor(this.m_ParticleSystem, ref gradient);
      return gradient;
    }
  }

In a nutshell, your ParticleSystem.MainModule object doesn't actually store anything. It's just a wrapper around the global, shared ParticleSystem object. Whenever you access a property on ParticleSystem.MainModule, it directly modifies the shared ParticleSystem. Hence the confusing behavior you're seeing.

Kevin Gosse
  • 38,392
  • 3
  • 78
  • 94
  • thanks of the great answer , but I need to ask you one final thing why did they do that ? , cant the mainModule be a class or cant be manipulated directly . – Honey Dec 17 '17 at 14:07
  • 1
    Why a struct? Because it saves an allocation when returning the wrapper (small performance gain). Why using wrappers? I honestly don't know. Unity is a huge mess, with a few singleton classes controlling everything. I guess they tried to give an object-oriented feeling by providing light wrappers that each give access to a subset of the huge parent class. – Kevin Gosse Dec 17 '17 at 14:10
2

Consider using ref keyword. With it you can pass any value type parameter by reference (not by copy).

https://www.dotnetperls.com/ref

Ref. A ref parameter is passed as a reference, not a value. This means you can assign the parameter in the called method and have it also be assigned at the calling site.

Consider also reading about out keyword

out. This C# keyword signifies a reference parameter. Sometimes methods must return more than one value and not store class state.

And do not get confused between them:

When to use ref vs out

Mohammed Noureldin
  • 14,913
  • 17
  • 70
  • 99