0

I have class A and B (just sample)

   public class A
    {
        public long Id { get; set; }
        public string Name { get; set; }
    }


    public class B : A
    {            
        public B(long id,string name)
        {

        }
    }

And want to do

 var b = new B(100, "myName");
 Save(b);

I have a save method that I want to allow only types inherited from A Class and also have uses the constructor that accept two parameters

// I know this will work if my Class B has public B() {}, 
//but not sure how to restrict to have only the once which accept constructor with two parameters           
 private void Save<T>(T target) where T : A, new ()
 {
       //do something
 }
huMpty duMpty
  • 14,346
  • 14
  • 60
  • 99
  • Id and Name are public from the base class. I was wondering why you need a constructor in B that passes these items in, if they can be set from the public interface of A anyway? Also, I'm unsure of the reasoning behind the "new()" part of the Save() method. If T subclasses A, then you should be fine to use that method with any instance of A, whether directly classed or subclassed. – ManoDestra Feb 24 '16 at 16:53
  • This is not the case, as I mentioned in question the provided classes are *"just sample"* for the question – huMpty duMpty Feb 24 '16 at 17:09
  • Ok, not a great sample then, but ignoring that, then a Save() method as you've declared it is fine, but without the "new()" part. So long as the T parameter is an A, then it should work fine. – ManoDestra Feb 24 '16 at 17:18
  • Yes, that what i need to avoid. However, below answers do the job :) – huMpty duMpty Feb 24 '16 at 17:22

2 Answers2

2

There's nothing in the C# type system that will enforce that constraint. You could use the reflection APIs to verify at runtime.

Another alternative would be to specify a factory:

interface IFactory<T> where T : A {
   T Construct(object param1, object param2)
}

class BFactory : IFactory<B> {
   public B Construct(object param1, object param2) {
       return new B(param1, param2);
   }
}

void Save<T>(T target, IFactory<T> tFactory) where T : A {
   T newT = tFactory.Construct(a, b);
}
Mark Brackett
  • 84,552
  • 17
  • 108
  • 152
  • `You could use the reflection APIs to verify at runtime` - You could create a unit test that uses reflection your code base adheres to your expectations, just make sure you document them so its understood what those expectations are. Also the unit test would only really work if this is **not** for an exported API, otherwise you would have to do some checks on API startup/load. – Igor Feb 24 '16 at 16:58
  • @Igor - that's a tough unit test to write; you'd need to find *callers* of the Save method and inspect their passed argument type, and then reflect the type to get constructor. It'd be possible, but I think you'd be looking at IL parsing at that point. NDepends CQL can probably do it in a one-liner if you really needed to.... – Mark Brackett Feb 24 '16 at 22:45
2

Generic constraints do not support constructors with parameters. Mostly a factory or creation function is used ( e.g. Is there a generic constructor with parameter constraint in C#? ), but since the object is created beforehand and you only want to filter which objects are allowed, a safer method is to implement an (empty) interface and use that as constraint:

   public class A
    {
        public long Id { get; set; }
        public string Name { get; set; }
    }


    public class B : A, IAmB
    {            
        public B(long id,string name)
        {

        }
    }

    public interface IAmB{}

That way the constraint would be:

private void Save<T>(T target) where T : A, IAmB
 {

 }
Community
  • 1
  • 1
Me.Name
  • 12,259
  • 3
  • 31
  • 48