In generics, constraints can accomplish two things for you and the user: 1. constraints can convert runtime errors to compilation errors; 2. the constraints clearly provide the functions that must be implemented by parameters of the instantiation type. However, the constraints do not limit what type parameters cannot do. Fortunately, in most cases, we don't need to worry about what additional features are included in the type parameter in addition to the necessary features.
However, if the generic constraint contains the new constraint, we need to check whether the generic parameter implements the idisposable interface.
Let's take a look at the following code.
Code
1 public interface IEngine
2 {
3 void DoWork();
4 }
5
6 public class EngineDriver<T> where T : IEngine, new()
7 {
8 public void GetThingsDone()
9 {
10 T driver = new T();
11 driver.DoWork();
12 }
13 }
After the above code is executed, if T implements the idisposable interface, it may cause resource leakage, because the constraints do not indicate that generic parameters need to implement the idisposable interface, therefore, from the compiler perspective, the dispose () method is not called. In this way, whenever the local variable is initialized, we need to check whether T has implemented the idisposable interface.
The following is the modified getthingsdone () method.
1 public void GetThingsDone()
2 {
3 T driver = new T();
4 using (driver as IDisposable)
5 {
6 driver.DoWork();
7 }
8 }
The above code will assign a local variable to the driver during compilation to store the result after the driver is forcibly converted to idisposable. If t does not implement the idisposable interface, the local variable is null, And the compiler will not call the dispose () method. If t implements the idisposable interface, the compiler generates a call to the dispose () method at the end of the code block.
Of course, to solve the problem above, we can modify the generic class to implement the idisposable interface. Let's look at the following code.
Code
1 public class EngineDriver<T> : IDisposable where T : IEngine, new()
2 {
3 private T driver = null;
4 public void GetThingsDone()
5 {
6 if (driver == null)
7 {
8 driver = new T();
9 }
10 driver.DoWork();
11 }
12
13 #region IDisposable Members
14
15 public void Dispose()
16 {
17 IDisposable temp = driver as IDisposable;
18 if (temp != null)
19 {
20 temp.Dispose();
21 }
22 }
23
24 #endregion
25 }
Note that the above Code does not guarantee that the dispose () method of the driver will not be repeatedly called to implement the class of the idisposable interface. The dispose () method must be called multiple times, because t does not have class constraints, you cannot set the driver to null before leaving the dispose () method.
Summary: Sometimes you can solve this problem by refactoring and removing instances from generic classes. When local variables must be used, the necessary code should be destroyed. If a generic class also needs to support delayed creation of generic parameters that implement the idisposable interface, more work will be done, but this is indispensable.