This is true - with some limitations on the eventing side in my personal opinion - but there are some caveats, which can easily be overcome if you understand the underlying workings of the proxies.
Consider the following example, in C#:
Query query = new Query();
QueryRun queryRun = new QueryRun(query);
/* .... */
At first glance, this would work. And it compiles as well. However, a runtime error occurs complaining that object of type "Query" does not have a method called "Next".
So what's happening? Well, remember the classes in managed code are proxies. So, they can be instantiated by giving them a reference to an existing X++ object. When you pass in your query object to the constructor of QueryRun, it will try to wrap the managed QueryRun proxy around the X++ object reference, which is the Query.
As you can see in the IntelliSense, the other overload of the constructor is to pass in an object of type "object". Not much documentation there, but that is in fact the X++ New() parameter profile we would like to invoke. So, we change our C# code to:
query = new Query();
queryRun = new QueryRun(query as object);
/* .... */
Now, this will work as expected.