0

Here's what I want to do:

using(var bmp = Surface.FromBmp("smile.bmp"))
{
    tex = Texture.FromSurface(ref rend, ref bmp);
}

Surface is a struct. I want to avoid making a copy of the struct just to pass it to Texture.FromSurface, so I'm passing it as a ref even though it's not modified. C# doesn't have a concept of const-refs, so I'm not sure what else I can do. Are there any elegant solutions?

I found Jon Skeet's answer, but it doesn't really offer a solution.

Community
  • 1
  • 1
mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • 1
    Are you sure that copying the Surface is actually bad? I can't believe that they build a struct to hold a complete surface, the Surface struct probably only holds something like a SurfaceHandle anyway. – nvoigt Jul 09 '13 at 05:34
  • @nvoigt: You're right..it's not as bad as I thought. It's about 6 ints, 4 pointers, ... and another struct with another 4 ints... I estimate it's about 60 bytes. It's coming from C though, I think some of the other guys are even bigger. – mpen Jul 09 '13 at 05:40
  • Standard .NET guidance is that a struct should not be larger than 16 bytes and have more than 4 fields. If you go past that then a struct stops being useful as a value type and should be replaced by a class. Using *ref* here is a C practice, it just isn't very appropriate in C#. – Hans Passant Jul 09 '13 at 09:52
  • `bmp` is necessarily a struct as it does come from C, although based on the answers I'm seeing here it seems like it might be better to wrap it in a class and drop the refs. – mpen Jul 09 '13 at 15:40

2 Answers2

1

You can write the same code using is using (try/finally) by hand. Something like:

IDisposable toClose = null;
try 
{
    var bmp = Surface.FromBmp("smile.bmp"));
    toClose = bmp;
    tex = Texture.FromSurface(ref rend, ref bmp);
}
finally 
{
    toClose.Dispose();
}
Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
1

The only type whose "struct-ness" would be a problem is that of bmp; are you saying that is a struct? If so, it would seem highly unusual that a struct would implement a non-trivial IDisposable.Dispose() method, but it's possible that the type is implemented as an "immutable" struct but encapsulates a mutable reference and behaves like one. In that case, I would suggest something like:

var bmp = Surface.FromBmp("smile.bmp");
try
{
  tex = Texture.FromSurface(ref rend, ref bmp);
}
bmp.Dispose(); // Or whatever method it exposes for such purpose

If the type in question actually requires disposal, it should expose a method for such purpose (some structure types like List<T>.Enumerator implement IDisposable because they are required to implement the interface, not because instances require cleanup). Do not cast to IDisposable, since that will create a new boxed instance of the structure; the cost of that will significantly exceed the cost of creating another struct instance. Another pattern you might be able to use would be:

var bmp = Surface.FromBmp("smile.bmp");
using(bmp)
{
  tex = Texture.FromSurface(ref rend, ref bmp);
}

since I think that form of the using statement creates its own private copy of its argument and will let you do what you like with the original, but there's no real reason why it should be necessary to have code make the extra copy of bmp, so I wouldn't particularly recommend that form. If for whatever reason bmp requires cleanup but does not expose any method for that purpose other than via IDisposable, you could do something like:

void CallDisposeOnRef<T>(ref T it) where T:IDisposable { it.Dispose(); }

and replace the last line of my first example with CallDisposeOnRef(ref bmp);, which would avoid having to make an extra copy of bmp in any fashion (the name is verbose to make clear that it only calls Dispose on the target; some people might expect such a method that takes a ref parameter to also set its argument to null)

supercat
  • 77,689
  • 9
  • 166
  • 211
  • I'm writing a wrapper around SDL, the dispose method simple calls [SDL_FreeSurface](http://sdl.beuc.net/sdl.wiki/SDL_FreeSurface). I thought it might be nice to have this in a disposable interface so that you can do things like I posted; load the BMP and then copy it into a texture and have it automatically cleaned up for you. While your solutions work, I don't think they make for a very nice API. I'm thinking it might be better to wrap the Surface struct in a class instead of exposing it directly. – mpen Jul 09 '13 at 15:46
  • 1
    @Mark: It's too bad some of the people involved in C# seem to fundamentally dislike structure types, and thus would rather say they shouldn't be used because of weaknesses in how they're handled, than fix those weaknesses. Depending upon how often they're created and recycled, it sounds as though it may be appropriate for your type to be a structure, and you sound like you have the instincts necessary to use structures appropriately. – supercat Jul 09 '13 at 16:31
  • 1
    @Mark: Immutable classes add GC pressure, which would limit use of your code to situations where long-lived objects are seldom modified to hold references to recently-generated ones (Gen0 collections are usually cheap, but the existence of many references from long-lived objects into shorter ones can significantly increase their cost). Mutable classes often have dodgy semantics. Using a struct may be appropriate, if you can deal with the quirks in C#'s handling of them. – supercat Jul 09 '13 at 16:37