質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.49%

C#でプリミティブ型をラップしたい

解決済

回答 4

投稿

  • 評価
  • クリップ 1
  • VIEW 4,731

score 27

前提・実現したいこと

C#でintやdoubleといったプリミティブ型をそのまま使わずラップしたい

void set_time(int hour, int minute, int second){...} ではなく
void set_time(Hour hour, Minute minute, Second second){...} のように
表現したい

該当のソースコード

    public class WrapperPrimitive<Type>
    {
        private readonly Type value;
        public WrapperPrimitive(Type value) { this.value = value; }
        public static explicit operator WrapperPrimitive<Type>(Type value) { return new WrapperPrimitive<Type>(value); }
        public static explicit operator Type(WrapperPrimitive<Type> temp) { return temp.value; }

        public override bool Equals(object obj)
        {
            if (obj == null) { return false; }
            if (this.GetType() != obj.GetType()) { return false; }
            WrapperPrimitive<Type> c = (WrapperPrimitive<Type>)obj;
            return (this.value == c.value);
        }

        //VSの補完が自動で作成 無いと警告が出るので残す
        public override int GetHashCode() { return base.GetHashCode(); }

    }


    public class Second: WrapperPrimitive<int>
    {
        public Second(int value) : base(value) { }
    }

発生している問題・エラーメッセージ

public override bool Equals(object obj)関数のreturn (this.value == c.value);箇所で

operator cannot be applied to operands of type 'Type' and 'Type'

試したこと

https://msdn.microsoft.com/ja-jp/library/ms173147(v=vs.90).aspx
等を参考に自分なりにoperator==をオーバーロードしてみましたがうまくいきませんでした。

補足情報(言語/FW/ツール等のバージョンなど)

いつも参考にさせていただいております。

C#初心者の為見当違いの質問でしたらお許しください。
上記案件について

・なぜうまくいかないか
・そもそもC#で型をラップするようなやり方は推奨されるのか

の2点についてご教授いただけると幸いです。
稚拙な質問でご迷惑かと思いますが宜しくお願い致します。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

checkベストアンサー

+2

技術者としてよくある、単に「やりたい」の回答として

using System;

namespace foo
{
    public class WrapperPrimitive<Type>
    {
        private readonly Type value;
        public WrapperPrimitive(Type value) { this.value = value; }
        public static explicit operator WrapperPrimitive<Type>(Type value) { return new WrapperPrimitive<Type>(value); }
        public static explicit operator Type(WrapperPrimitive<Type> temp) { return temp.value; }

        public override bool Equals(object obj)
        {
            if (obj == null) { return false; }
            if (this.GetType() != obj.GetType()) { return false; }
            WrapperPrimitive<Type> c = (WrapperPrimitive<Type>)obj;
            return this.value.Equals(c.value);
        }

        public static bool operator ==(WrapperPrimitive<Type> lhs, WrapperPrimitive<Type> rhs)
        {
            return lhs.Equals(rhs);
        }

        public static bool operator !=(WrapperPrimitive<Type> lhs, WrapperPrimitive<Type> rhs)
        {
            return !lhs.Equals(rhs);
        }

        //VSの補完が自動で作成 無いと警告が出るので残す
        public override int GetHashCode() { return base.GetHashCode(); }
    }


    public class Second: WrapperPrimitive<int>
    {
        public Second(int value) : base(value) { }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Second foo = new Second(1);
            Second bar = new Second(1);
            if (foo == bar) {
                Console.WriteLine("Yes We Can");
            }
        }
    }
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/06/26 19:21

    皆さん非常に丁寧に解説して下さり非常に助かりました、ありがとうございました。
    甲乙つけ難かったのですが技術的な解決策を提示して下さったmattnさんをベストアンサーに
    選ばせていただきました。 
    ありがとうございました。

    キャンセル

+1

operatorは、プログラミングの本をよむと書いてあるけど、ほぼ使わない。言語仕様として、ライブラリを作る上級者には必要なものだけど、そうでもないなら使わない。
汎用性のあるoperatorは難易度高い。やるなら、それぞれの型ごとに作ったほうがいいけど、ただ面倒なだけ。

はじめの問題に対応するだけなら、
適切にクラスを作ればいいだけと思います。

        public class DateObject
        {
            public int Hour { get; set; }
            public int Minute { get; set; }
            public int Second { get; set; }
        }

        public void Set_Time(DateObject date)
        {

        }

呼び出し方

            Set_Time(new DateObject() { Hour = 22, Minute = 12, Second = 21 });

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

mattnさんがエラーを解消する方法について回答されていますが、追加でコメントしてみます。

intやdoubleなどのプリミティブをジェネリクスでラップしたいならしかるべき配慮を怠らないように言語仕様を注意深く調べる必要があると思います。

  • 値の同一性
    GetHashCodeを「無いと警告が出るので残す」という理由で自動生成されたbase.GetHashCode()のまま放置されてますが、それはいただけません。自動生成された内容はあくまで基底クラスの動作と同じにするという内容ですが、本件では「同じ値を持つ異なるインスタンスのハッシュ値が違う値になる」という病的な結果となるため正しいとは言えません。とりあえず以下のように矛盾が生じない定義にしておく必要があると思います。
class ... {
  public override int GetHashCode() {
    return value.GetHashCode();
  }
}

その他:

  • 型引数に対する制約
    Typeには何でも指定できてしまいますがprimitive型を想定しているなら、できる限り型引数の制約を書いておいた方がよいのではないでしょうか。
class WrappedPrimitive<T> where T: struct {
}
  • 演算子
    プリミティブ型のラップ型ならば、いくつかの演算子が使えないと(C#では)不自然に感じる場合もあると思います。時、分、秒なら==, !=, <, <=, >, >=あたりはほしいのではないでしょうか?しかしながら四則演算に対して閉じてない(値域が定義域とは異なるという意味です)ので四則演算は下手に定義できない気がします。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

単純に型TをWrapして別名を付けると言うことはC#ではまずしないです。
恐らく、C言語あたりの#define あたりを企図してかのコトかと思いますが、C#では別名を付ける強い理由もなくまた、余計な混乱を生むだけなので行われた事例を寡聞にして知りません。

さて、立ち戻って、掲示されたコードを拝見するに、WrapperPrimitive型には元のPrimitive型の元値がどこにも保持されておらず、そもそも==のしようがないという問題があります。
また、C#においては、型引数Tに対してPrimitiveのみを受容すると言うことをコンパイルタイムに弁別することは不可能なので、名前だけになっており可否の判断以前にこのようなクラスを使うべきではないと考えます。(さらに申し上げれば、値型をわざわざオレオレBoxingする必要もありますまい)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.49%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る