3

This might sound stupid. We know we can assign value to a string variable as following.
String name = "myname";

String is a reference Type, but don't require new operator while declaring and assigning value. If I want to design a custom class with such a behavior, how would I proceed?

Thanks

Andre Kampling
  • 5,476
  • 2
  • 20
  • 47
Anu Viswan
  • 17,797
  • 2
  • 22
  • 51
  • 2
    please review struct vs class - but these are primitive types not really classes.. – user230910 Jul 27 '17 at 11:38
  • 1
    String is a special case: [Why don't we use new operator while initializing a string?](https://stackoverflow.com/questions/3328707/why-dont-we-use-new-operator-while-initializing-a-string) – Alex K. Jul 27 '17 at 11:39
  • @user230910 string is reference type rt ? how does it do it ? – Anu Viswan Jul 27 '17 at 11:42
  • 1
    @AnuViswan string is a special case. Basically it is an immutable char array (well not quite, it's even worse using pointers etc.) and as such it is a reference type and not a value type. It does however implement both `==` and `!=` operator with value checking as you can see at [Reference Source](https://referencesource.microsoft.com/mscorlib/R/11648d2d83718c5e.html). That's why it feels a little like a value type. – Adwaenyth Jul 27 '17 at 11:50

5 Answers5

5

What you are looking for is an implicit type conversion method (Microsoft Documentation). As an example, assuming you have a class called 'PositiveFloat' that automatically clamps floats to values >= 0, then you could have the following class layout:

class PositiveFloat
{
    public float val = 0.0f;

    public PositiveFloat(float f)
    {
        val = Math.Max(f, 0.0f); //Make sure f is positive
    }

    //Implicitly convert float to PositiveFloat
    public static implicit operator PositiveFloat(float f)
    {
        return new PositiveFloat(f);
    }

    //Implicitly convert PositiveFloat back to a normal float
    public static implicit operator float(PositiveFloat pf)
    {
        return pf.val;
    }
}

//Usage
PositiveFloat posF = 5.0f; //posF.val == 5.0f
float fl = posF; //Converts posF back to float. fl == 5.0f
posF = -15.0f; //posF.val == 0.0f - Clamped by the constructor
fl = posF; //fl == 0.0f

For this example, you'll also probably want to provide implicit operator methods for +, - etc to support float and int arithmetic on this class.

The operators aren't just restricted to core data types like int though, you can implicitly make one class from another just by using '=', but this gets to the point where you need to start judging context. Does Thing t = y; make sense, or should it be Thing t = new Thing(y); or even Thing t = y.ConvertToThing();? That's up to you.

At the core of C#, basic data types like int, float, char etc are implemented at the compiler level just so we have some sort of base to work with. string too, even though it appears like a reference type. How these types work with stuff like operators and such are virtually identical to the implicit operator stuff above though to ensure consistency, as well as allowing you to invent your own 'basic' types entirely in C#.

Community
  • 1
  • 1
  • woah this is cool, i guess it may be possible to do this from a "object" or anonymous type, so you could make a Myperson person = new { "name" : "joe" }; kind of assignment – user230910 Jul 27 '17 at 11:46
  • No, that is wrong @GoodNightNerdPride. `int i = 1` does no implicit type conversion. It is just an assignment implemented in the compiler. – Patrick Hofman Jul 27 '17 at 11:46
  • 1
    No, it's not wrong @PatrickHofman. Check the question again. OP is not asking for implementation details of the compiler, he is asking how to achieve the same _behavior_. – Good Night Nerd Pride Jul 27 '17 at 11:47
  • I wonder if `PositiveFloat posF = -15.0f;` shouldn't be `15` instead of `0` ;-) – Tim Schmelter Jul 27 '17 at 11:55
  • I would mark this as anwer. As @GoodNightNerdPride rightly mentioned, I wasn't looking for implementation details within compiler, but rather, how I can replicate same behavior. Thanks – Anu Viswan Jul 27 '17 at 11:57
1

Built-in "compiler magic" deals with it. Under the hood, literals for strings, integers, longs, etc. get converted to operations for properly creating the corresponding object or value at run time, without you having to use operator new.

Both String and Int32 are classes

Technically, Int32 is a struct, not a class, but the same logic applies.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
1

You can achieve this with your own classes using implicit cast operators - basically, you define a static method in your class that can create an instance from a value. Check this:

public class MyClass
{
   public static implicit operator MyClass (int val)
   {
       return new MyClass { Value = new String('!', val)};
   }
   public string Value {get;set;}
}

This class holds a string. When implicitly casting from an int, it will initialize the class with the int number of exclamation marks:

MyClass obj = 5;
Console.WriteLine(obj.Value); // outputs !!!!!
Avner Shahar-Kashtan
  • 14,492
  • 3
  • 37
  • 63
1

This feature is called literal constants. The compiler supports these for only some types, most of them are what we informally call primitve types. These are:

  1. Boolean literals: true and false.

  2. Integer literals: int, uint, long, and ulong.

    The exact type of the literal is the specified type through the literal's suffix or inferred from the value of the literal (inferred type will always be a signed type).

    If the inferred type is not the declared type of the variable, an implicit conversion is automatically performed if available (otherwise a compile time error is raised).

  3. Real literals: float, double and decimal

    The type of the literal will always be double unless otherwise specified by the literal's suffix. Again, an implicit conversion to the variables declared type will be performed if necessary and available.

  4. Character literals: 'a', '1', '\u1000', etc.

  5. String literals: two types of literal strings are supported: regular string literals and verbatim string literals: "C:\\Path" and @"C:\Path".

  6. The null literal: null

All these have specific support form the compiler. You can not in any way implement your own type witht his kind of behavior. What you can do is implement an implicit conversion from one of the above literals (except the null literal) to your type and mimick in someway literal constants.

Do bear in mind that you only mimick with visual sugar, because they are most definitely not literal constants:

struct MyLiteralType
{
     public static implicit MyLiteralType(int i) => new MyLiteralType(i);
}

const MyLiteralType myLiteralTypeConstant = 1; //--> compile time error.
InBetween
  • 32,319
  • 3
  • 50
  • 90
-1

You cannot design a class with such behavior. But you can design a struct. And you do not need to do anything special: A struct can always be instantiated by just declaring it.

As for string s = "literal", string is a class, but its instantiation using a literal is handled specially by the compiler. It is equivalent to string s = new string( literal ).

Mike Nakis
  • 56,297
  • 11
  • 110
  • 142