Csharp/C Sharp/Generics/Generic Constraint

Материал из .Net Framework эксперт
Перейти к: навигация, поиск

A base class constraint

using System;
class MyBase {
  public void hello() {
    Console.WriteLine("Hello");
  }
}
class B : MyBase { }
class C { }
class Test<T> where T : MyBase {
  T obj;
  public Test(T o) {
    obj = o;
  }
  public void sayHello() {
    obj.hello();
  }
}
class BaseClassConstraintDemo {
  public static void Main() {
    MyBase a = new MyBase();
    B b = new B();
    C c = new C();
    Test<MyBase> t1 = new Test<MyBase>(a);
    t1.sayHello();
    Test<B> t2 = new Test<B>(b);
    t2.sayHello();
    // The following is invalid because
    // C does not inherit MyBase.
    // Test<C> t3 = new Test<C>(c); // Error!
  }
}


A new() constructor constraint

using System;
class MyClass {
  public MyClass() {
  }
}
class Test<T> where T : new() {
  T obj;
  public Test() {
    // This works because of the new() constraint.
    obj = new T(); // create a T object
  }
}
class ConsConstraintDemo {
  public static void Main() {
    Test<MyClass> x = new Test<MyClass>();
  }
}


A type parameter can be used as a constraint

    using System;
    using System.Collections;
    public class Test{
        public static void Main(){
           X<Y, Z> obj=new X<Y, Z>();
        }
    }
    public class Z {
        public void MethodA() {
            Console.WriteLine("Y::MethodA");
        }
    }
    public class Y: Z {
    }
    public class X<T1, T2> where T1:T2 {
        public void MethodB(T1 arg) {
        }
    }


Combination of Overriding Generic Methods

 
/*
Base Method    Derived Method        Comments
Nongeneric     Generic (open)        Permitted
Nongeneric     Generic (closed)      Permitted
Generic (open) Nongeneric            Not permitted
Generic (open) Generic (open)        Permitted; must use the same type parameters
Generic (open) Generic (closed)      Not permitted
Generic (closed) Nongeneric          Permitted
Generic (closed) Generic (closed)    Permitted
Generic (closed)  Generic (open)     Not permitted
*/


constructor constraint

 
using System;

public class Starter {
    public static void Main() {
        MyClass obj = new MyClass();
        obj.MethodA<XClass>();
    }
}
public class MyClass {
    public void MethodA<T>()
                 where T : XClass, new() {
        Console.WriteLine("MyClass.MethodA");
        T obj = new T();
        obj.MethodB();
    }
}
public class XClass {
    public void MethodB() {
        Console.WriteLine("XClass.MethodB");
    }
}


Default Constructor Constraint

    using System;
    public class Test{
        public static void Main(){
            Z obj=new Z();
            obj.MethodA<X>();
        }
    }
    public class Z {
        public void MethodA<T>() where T:X, new() {
            Console.WriteLine("Z.MethodA");
            T obj=new T();
            obj.MethodB();
        }
    }
    public class X{
        public void MethodB() {
            Console.WriteLine("X.MethodB");
        }
    }


Demonstrate a reference constraint

using System;
class MyClass {
}
class Test<T> where T : class {
  T obj;
  public Test() {
    // The following statement is legal only
    // because T is guaranteed to be a reference
    // type, which can be assigned the value null.
    obj = null;
  }
  public void print(){
     Console.WriteLine(obj);
  }
}
class ClassConstraintDemo {
  public static void Main() {
    Test<MyClass> x = new Test<MyClass>();
    // The next line is in error because int is
    // a value type.
//    Test<int> y = new Test<int>();
  }
}


Demonstrate a value type constraint

using System;
struct MyStruct {
}
class MyClass {
}
class Test<T> where T : struct {
  T obj;
  public Test(T x) {
    obj = x;
  }
}
class Test {
  public static void Main() {
    Test<MyStruct> x = new Test<MyStruct>(new MyStruct());
    Test<int> y = new Test<int>(10);
    // But, the following declaration is illegal!
//    Test<MyClass> z = new Test<MyClass>(new MyClass());
  }
}


Derivation Constraint

 
using System;
public class Starter {
    public static void Main() {
        // good
        MyClass<XClass, YClass> obj = new MyClass<XClass, YClass>();
        // good
        MyClass<XClass, WClass> obj2 = new MyClass<XClass, WClass>();
        // bad
        MyClass<WClass, YClass> obj3 = new MyClass<WClass, YClass>();
    }
}
public class MyClass<K, V>
    where K : XClass
    where V : YClass {
}
public class XClass {
}
public class YClass {
}
public class WClass : YClass {
}


Reference Type Constraint

    using System;
    using System.Collections;
    public class Test{
        public static void Main(){
            Z<X> obj2=new Z<X>();
        }
    }
    public class Z<T> where T: class {
        public void Iterate(T data) {
        }
    }
    public class X{
    }


Reference Type Constraint: A reference type constraint restricts a type parameter to a reference type.

 
using System;
using System.Collections;

public class Starter {
    public static void Main() {
        // MyClass<int> obj1=new MyClass<int>(); [illegal]
        MyClass<XClass> obj2 = new MyClass<XClass>();
    }
}
public class MyClass<T> where T : class {
    public void Iterate(T data) {
    }
}
public class XClass {
}


There are five types of constraints:

 
/*
    *Derivation constraints state the ascendancy of a type parameter.
    *Interface constraints are interfaces that are implemented by the type parameter.
    *Value type constraints restrict a type parameter to a value type.
    *Reference type constraints restrict a type parameter to a reference type.
    *Constructor constraints stipulate that the type parameter has a default or parameterless constructor.
*/


Use an interface constraint

using System;
class NotFoundException : ApplicationException { }
public interface IPhoneNumber {
  string Number {
    get;
    set;
  }
  string Name {
    get;
    set;
  }
}
class Friend : IPhoneNumber {
  string name;
  string number;
  public Friend(string n, string num) {
    name = n;
    number = num;
  }
  public string Number {
    get { return number; }
    set { number = value; }
  }
  public string Name {
    get { return name; }
    set { name = value; }
  }
}
class Supplier : IPhoneNumber {
  string name;
  string number;
  public Supplier(string n, string num) {
    name = n;
    number = num;
  }
  public string Number {
    get { return number; }
    set { number = value; }
  }
  public string Name {
    get { return name; }
    set { name = value; }
  }
}
class EmailFriend {
}
class PhoneList<T> where T : IPhoneNumber {
  T[] phList;
  int end;
  public PhoneList() {
    phList = new T[10];
    end = 0;
  }
  public bool add(T newEntry) {
    if(end == 10) return false;
    phList[end] = newEntry;
    end++;
    return true;
  }
  public T findByName(string name) {
    for(int i=0; i<end; i++) {
      if(phList[i].Name == name)
        return phList[i];
    }
    throw new NotFoundException();
  }
  public T findByNumber(string number) {
    for(int i=0; i<end; i++) {
      if(phList[i].Number == number)
        return phList[i];
    }
    throw new NotFoundException();
  }
}
class Test {
  public static void Main() {
    PhoneList<Friend> plist = new PhoneList<Friend>();
    plist.add(new Friend("A", "555-1111"));
    plist.add(new Friend("B", "555-6666"));
    plist.add(new Friend("C", "555-9999"));
    try {
      Friend frnd = plist.findByName("B");
      Console.Write(frnd.Name + ": " + frnd.Number);
    } catch(NotFoundException) {
      Console.WriteLine("Not Found");
    }
    Console.WriteLine();
    PhoneList<Supplier> plist2 = new PhoneList<Supplier>();
    plist2.add(new Supplier("D", "555-4444"));
    plist2.add(new Supplier("E", "555-3333"));
    plist2.add(new Supplier("F", "555-2222"));
    try {
      Supplier sp = plist2.findByNumber("555-2222");
      Console.WriteLine(sp.Name + ": " + sp.Number);
    } catch(NotFoundException) {
        Console.WriteLine("Not Found");
    }
    //PhoneList<EmailFriend> plist3 = new PhoneList<EmailFriend>(); 
  }
}