5

I want to create a constructor that only accepts primitive types, how can I do this?

Like this example:

public Test(PrimitiveType type)
{

}

I need do it in a constructor and it's optional, so I want to create a parameterless constructor and a constructor with the parameter.

Jeroen Vannevel
  • 43,651
  • 22
  • 107
  • 170
Only a Curious Mind
  • 2,807
  • 23
  • 39
  • 3
    It's not possible. Why do you need that anyway? – Thomas Levesque Jun 11 '14 at 18:23
  • Perhaps an overloaded method that can only accept the primitive types i.e. `Method(char a)` and `Method(int a)` – 3dd Jun 11 '14 at 18:23
  • Maybe one way would be to use [dynamic](http://msdn.microsoft.com/en-gb/library/dd264736.aspx) and then check the type of it, if it's a primitive type carry on executing, if not throw an exception. – Sam Jun 11 '14 at 18:24
  • 1
    C# has no inherent concept of primitive types to the language. You'll need to define what you mean by "primitive type". If you want to simply have a method that accepts any type in a finite set of types known at compile time, the solution is to use overloads. – Servy Jun 11 '14 at 18:25
  • 3
    @Sam: Why dynamic? The good old System.Object will provide this functionality – Omer Jun 11 '14 at 18:26
  • @ThomasLevesque to validate at compile time. – Only a Curious Mind Jun 11 '14 at 18:27
  • 1
    @Omer Actually you should just use a generic method that's not constrained. That, unlike either of your options, would avoid boxing. – Servy Jun 11 '14 at 18:27
  • 1
    @Omer I've been working with dynamic quite heavily over the past few days, I guess it was just first on my mind. – Sam Jun 11 '14 at 18:27
  • 1
    @LucasAbilidebob That's not an answer to Thomas' question. That's what you want to do, not why you want to do it. – Servy Jun 11 '14 at 18:28
  • @Servy Is because I'm creating a framework and need to validate it at compile time and not runtime. – Only a Curious Mind Jun 11 '14 at 18:32
  • You still haven't clarified it. What is it you're going to do with this primitive type? – Jeroen Vannevel Jun 11 '14 at 18:33
  • Are you trying to avoid mistakes in your own code or do you need to guarantee that no-one can pass an instance of a non-primitive type at compile time? – Lee Jun 11 '14 at 18:33
  • 1
    @LucasAbilidebob Once again, that's stating *what* you want to do. You haven't answered the question of why you want to constrain that type top be a primitive type at compile time, you've just said *that* you want it. I'll also repeat my earlier comment in saying that C# has no such concept as "primitive types", you need to define what you mean by the term, because it has no inherent meaning in C#. – Servy Jun 11 '14 at 18:34
  • @Lee I need to guarantee that no-one can pass an instance of a non-primitive type at compile time because it will not work – Only a Curious Mind Jun 11 '14 at 18:36
  • @Servy http://msdn.microsoft.com/en-us/library/aa711900(v=vs.71).aspx documentation about primitive types. – Only a Curious Mind Jun 11 '14 at 18:38
  • 1
    @LucasAbilidebob - _What_ won't work? Knowing a value is 'primitive' doesn't let you do anything with it apart from the methods on `object`. – Lee Jun 11 '14 at 18:38
  • @LucasAbilidebob You just linked to the VB.NET definition of a primitive type. VB.NET *does* have the concept of primitive types. C# does not. If you want to use that definition that's fine, just realize that you're applying the definition of a concept from another language. That definition will differ from other options, such as, for example the, CLR definition of a primitive (which is what some of the comments/answers have used in the form of `Type.IsPrimitive`). To give an example, in VB.NET `string` is a primitive. In the CLR it is not. – Servy Jun 11 '14 at 18:39

4 Answers4

11

Depending upon what you want to achieve, you might want to look at so-called "convertible types", e.g. the types that implement IConvertible interface, those are the following:

  • Boolean,
  • SByte,
  • Byte,
  • Int16,
  • UInt16,
  • Int32,
  • UInt32,
  • Int64,
  • UInt64,
  • Single,
  • Double,
  • Decimal,
  • DateTime,
  • Char, and
  • String.

So, as you see, this covers pretty much of what you'd like to achieve with primitive types.

So, by writing the method like that

public void Test(IConvertible primitive)
{
   if (primitive is Double) ....
   if (primitive is String) ....
}

you will confine your input types to the following ones (no structs etc).

Alternatively, you can also implement it as a generic method:

public void Test<T>(T primitive) where T : IConvertible
{
   if (primitive is Double) ....
   if (primitive is String) ....
}

Since you put this constrain, you can always convert your type to one, like:

public void Test<T>(T primitive) where T : IConvertible
{
   var myval = Convert.ToDecimal(primitive);
   ....
}
Alexander Galkin
  • 12,086
  • 12
  • 63
  • 115
  • 3
    Although there are plenty of types that implement `IConvertible` that aren't primitives. – Servy Jun 11 '14 at 18:30
  • It's a good way but my method is a constructor! :( I edited my answer – Only a Curious Mind Jun 11 '14 at 18:31
  • This is equally incomplete as using `where T : struct` though. If you're going to be using a runtime solution, then you might as well just do `typeof(T).IsPrimitive`. – Jeroen Vannevel Jun 11 '14 at 18:32
  • @LucasAbilidebob If you are satisfied with `IConvertible`, then use just the first code with `IConvertible` as parameter type and perform an additional run-time check within your constructor. – Alexander Galkin Jun 11 '14 at 18:38
3

There is no way to do it with a single overload¹ (but you could write overloads for each primitive type, of course). You can make it accept only value types, but then it will accept any struct, not just primitive types.


¹ well, it's not possible to enforce it at compile time, but you can check the type at runtime and throw an exception of course...

Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
2

EDIT: I Was completely mistaken in my last answer. Please see the following

Type class does in fact bear an IsPrimitive property. This will allow you to check against the class data if it is primitive.

As with anything you need to take in more than one acceptable type, you'll need to make it generic, severely limiting some options at runtime.

public void SomeMethod<T>(T obj)
{
    if(typeof(T).IsPrimitive == false) throw new ArgumentException("obj");
}

UPDATE: If this is a constructor, you can accept any object type. Otherwise, you'll have to make your class generic for that parameter

// Constructor for SomeClass
public SomeClass(object obj)
{
    if(obj.GetType().IsPrimitive == false) throw new ArgumentException("obj");
}

EDIT: So this is still getting updates and I'd like to point out a very big reason why this sort of approach probably isn't good architecture; it's possible to construct new types that function like primitive types. System.Numerics has a bunch of these nonprimitive, mathematical, struct types, my favorite being BigInteger, but more common being Vector2 or similar implementations.

David
  • 10,458
  • 1
  • 28
  • 40
  • 4
    `if (!typeof(T).IsPrimitive)` would be terser ;) – Thomas Levesque Jun 11 '14 at 18:28
  • 1
    It's a good way but my method is a constructor! :( I edited my answer – Only a Curious Mind Jun 11 '14 at 18:29
  • @ThomasLevesque That assumes that the generic argument is the same type as the parameter itself. That may not be the case. For example, someone could write: `SomeMethod(5);` – Servy Jun 11 '14 at 18:32
  • @Servy, indeed. On the other hand, you could add a `struct` constraint on T, which would solve the problem. – Thomas Levesque Jun 11 '14 at 18:47
  • 1) This doesn't validate the argument statically, as is requested in the question 2) the use of `object` boxes the value, unlike a generic solution 3) [it's not clear that the OP's definition of "primitive" is in line with `Type.IsPrimitive`](http://stackoverflow.com/questions/24169901/how-to-constructor-accept-only-primitive-types?noredirect=1#comment37305782_24169901) – Servy Jun 11 '14 at 18:48
  • @ThomasLevesque [Not if the OP's definition of "primitive" includes reference types](http://stackoverflow.com/questions/24169901/how-to-constructor-accept-only-primitive-types/24170007?noredirect=1#comment37305724_24169901) – Servy Jun 11 '14 at 18:49
1

I have given a very similar solution before but using a template you can generate multiple overloads that use the different types that you want (at compiletime).

<#@ template language="C#" #>
<#@ output extension=".cs" #>
<#@ import namespace="NamespaceProofOfConcept" #>
<#@ assembly name="$(TargetPath)" #>


<# Type[] types = new[] {
    typeof(byte), typeof(short), typeof(int),
    typeof(long), typeof(float), typeof(double),
    typeof(bool), typeof(DateTime), typeof(char),
    typeof(string)
    };
#>

using System;
namespace NamespaceProofOfConcept {
public partial class Test {
<# foreach (var type in types) { 
#>
    public Test(<#= type.Name #> value) {
        doConstructorStuff(value);
    }
<#
} #>

    private void doConstructorStuff(object o){

    }
} 
}

This will generate the following class:

using System;

namespace NamespaceProofOfConcept {
    public partial class Test {
        public Test(Byte value) {
            doConstructorStuff(value);
        }
        public Test(Int16 value) {
            doConstructorStuff(value);
        }
        public Test(Int32 value) {
            doConstructorStuff(value);
        }
        public Test(Int64 value) {
            doConstructorStuff(value);
        }
        public Test(Single value) {
            doConstructorStuff(value);
        }
        public Test(Double value) {
            doConstructorStuff(value);
        }
        public Test(Boolean value) {
            doConstructorStuff(value);
        }
        public Test(DateTime value) {
            doConstructorStuff(value);
        }
        public Test(Char value) {
            doConstructorStuff(value);
        }
        public Test(String value) {
            doConstructorStuff(value);
        }

        private void doConstructorStuff(object o){

        }
    } 
}

Now you can only call this constructor with the types you defined (which are essentially the VB.NET primitive types).

You can add functionality to this by creating your own partial class which defines the behaviour:

namespace NamespaceProofOfConcept
{
    public partial class Test
    {
        public void DoAThing()
        {
            System.Console.WriteLine("custom method!");
        }
    }
} 

And you can test it out with the following code (which outputs "custom method!"):

static void Main(string[] args)
{
    Test obj = new Test(true);
    obj.DoAThing();
    Console.ReadKey();
}

If you want even more "security", you can define the parameterless constructor as private inside your T4 template so it cannot be added unless the template is modified.

Community
  • 1
  • 1
Jeroen Vannevel
  • 43,651
  • 22
  • 107
  • 170